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 vh...@apache.org on 2009/12/22 18:20:53 UTC

svn commit: r893238 [3/4] - in /xmlgraphics/fop/trunk: src/java/org/apache/fop/area/ src/java/org/apache/fop/fo/properties/ src/java/org/apache/fop/layoutmgr/ src/java/org/apache/fop/layoutmgr/inline/ src/java/org/apache/fop/layoutmgr/list/ src/java/or...

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java?rev=893238&r1=893237&r2=893238&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java Tue Dec 22 17:20:51 2009
@@ -19,6 +19,7 @@
 
 package org.apache.fop.layoutmgr.inline;
 
+import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.ListIterator;
@@ -59,6 +60,9 @@
 
     //TODO: remove all final modifiers at local variables
 
+    // static final int SOFT_HYPHEN_PENALTY = KnuthPenalty.FLAGGED_PENALTY / 10;
+    private static final int SOFT_HYPHEN_PENALTY = 1;
+
     /**
      * Store information about each potential text area.
      * Index of character which ends the area, IPD of area, including
@@ -66,25 +70,27 @@
      * Number of word-spaces?
      */
     private class AreaInfo {
+
         private final int startIndex;
         private final int breakIndex;
         private final int wordSpaceCount;
         private int letterSpaceCount;
-        private final MinOptMax areaIPD;
+        private MinOptMax areaIPD;
         private final boolean isHyphenated;
         private final boolean isSpace;
         private boolean breakOppAfter;
         private final Font font;
 
         AreaInfo(final int startIndex,
-                        final int breakIndex,
-                        final int wordSpaceCount,
-                        final int letterSpaceCount,
-                        final MinOptMax areaIPD,
-                        final boolean isHyphenated,
-                        final boolean isSpace,
-                        final boolean breakOppAfter,
-                        final Font font) {
+                 final int breakIndex,
+                 final int wordSpaceCount,
+                 final int letterSpaceCount,
+                 final MinOptMax areaIPD,
+                 final boolean isHyphenated,
+                 final boolean isSpace,
+                 final boolean breakOppAfter,
+                 final Font font) {
+            assert startIndex <= breakIndex;
             this.startIndex = startIndex;
             this.breakIndex = breakIndex;
             this.wordSpaceCount = wordSpaceCount;
@@ -96,28 +102,38 @@
             this.font = font;
         }
 
-        public String toString() {
-            return "[ lscnt=" + this.letterSpaceCount
-                + ", wscnt=" + this.wordSpaceCount
-                + ", ipd=" + this.areaIPD.toString()
-                + ", sidx=" + this.startIndex
-                + ", bidx=" + this.breakIndex
-                + ", hyph=" + this.isHyphenated
-                + ", space=" + this.isSpace
-                + ", font=" + this.font
-                + "]";
+        private int getCharLength() {
+            return breakIndex - startIndex;
+        }
+
+        private void addToAreaIPD(MinOptMax idp) {
+            areaIPD = areaIPD.plus(idp);
         }
 
+        public String toString() {
+            return "AreaInfo["
+                    + "letterSpaceCount = " + letterSpaceCount
+                    + ", wordSpaceCount = " + wordSpaceCount
+                    + ", areaIPD = " + areaIPD
+                    + ", startIndex = " + startIndex
+                    + ", breakIndex = " + breakIndex
+                    + ", isHyphenated = " + isHyphenated
+                    + ", isSpace = " + isSpace
+                    + ", font = " + font
+                    + "]";
+        }
     }
 
-    // this class stores information about changes in vecAreaInfo
-    // which are not yet applied
+    /**
+     * this class stores information about changes in vecAreaInfo which are not yet applied
+     */
     private final class PendingChange {
-        private final AreaInfo ai;
+
+        private final AreaInfo areaInfo;
         private final int index;
 
-        private PendingChange(final AreaInfo ai, final int index) {
-            this.ai = ai;
+        private PendingChange(final AreaInfo areaInfo, final int index) {
+            this.areaInfo = areaInfo;
             this.index = index;
         }
     }
@@ -128,14 +144,11 @@
     private static final Log LOG = LogFactory.getLog(TextLayoutManager.class);
 
     // Hold all possible breaks for the text in this LM's FO.
-    private final List vecAreaInfo;
+    private final List areaInfos;
 
     /** Non-space characters on which we can end a line. */
     private static final String BREAK_CHARS = "-/";
 
-    /** Used to reduce instantiation of MinOptMax with zero length. Do not modify! */
-    private static final MinOptMax ZERO_MINOPTMAX = new MinOptMax(0);
-
     private final FOText foText;
 
     /**
@@ -155,18 +168,23 @@
     private MinOptMax letterSpaceIPD;
     /** size of the hyphen character glyph in current font */
     private int hyphIPD;
-    /** 1/1 of word-spacing value */
-    private SpaceVal ws;
 
     private boolean hasChanged = false;
     private int returnedIndex = 0;
     private int thisStart = 0;
     private int tempStart = 0;
-    private List changeList = null;
+    private List changeList = new LinkedList();
 
     private AlignmentContext alignmentContext = null;
 
+    /**
+     * The width to be reserved for border and padding at the start of the line.
+     */
     private int lineStartBAP = 0;
+
+    /**
+     * The width to be reserved for border and padding at the end of the line.
+     */
     private int lineEndBAP = 0;
 
     private boolean keepTogether;
@@ -178,49 +196,37 @@
      *
      * @param node The FOText object to be rendered
      */
-    public TextLayoutManager(final FOText node) {
-        super();
-        this.foText = node;
-
-        this.letterAdjustArray = new MinOptMax[node.length() + 1];
-
-        this.vecAreaInfo = new java.util.ArrayList();
-    }
-
-    private KnuthPenalty makeZeroWidthPenalty(final int penaltyValue) {
-        return new KnuthPenalty(
-                0,
-                penaltyValue,
-                false,
-                this.auxiliaryPosition,
-                true);
+    public TextLayoutManager(FOText node) {
+        foText = node;
+        letterAdjustArray = new MinOptMax[node.length() + 1];
+        areaInfos = new ArrayList();
+    }
+
+    private KnuthPenalty makeZeroWidthPenalty(int penaltyValue) {
+        return new KnuthPenalty(0, penaltyValue, false, auxiliaryPosition, true);
     }
 
     private KnuthBox makeAuxiliaryZeroWidthBox() {
-        return new KnuthInlineBox(
-                0,
-                null,
-                this.notifyPos(new LeafPosition(this, -1)),
-                true);
+        return new KnuthInlineBox(0, null, notifyPos(new LeafPosition(this, -1)), true);
     }
 
     /** {@inheritDoc} */
     public void initialize() {
 
-        this.foText.resetBuffer();
+        foText.resetBuffer();
 
-        this.spaceFont = FontSelector.selectFontForCharacterInText(' ', this.foText, this);
+        spaceFont = FontSelector.selectFontForCharacterInText(' ', foText, this);
 
-        // With CID fonts, space isn't neccesary currentFontState.width(32)
-        this.spaceCharIPD = this.spaceFont.getCharWidth(' ');
-        // Use hyphenationChar property
+        // With CID fonts, space isn't necessary currentFontState.width(32)
+        spaceCharIPD = spaceFont.getCharWidth(' ');
 
+        // Use hyphenationChar property
         // TODO: Use hyphen based on actual font used!
-        this.hyphIPD = this.foText.getCommonHyphenation().getHyphIPD(this.spaceFont);
+        hyphIPD = foText.getCommonHyphenation().getHyphIPD(spaceFont);
 
-        final SpaceVal ls = SpaceVal.makeLetterSpacing(this.foText.getLetterSpacing());
-
-        this.ws = SpaceVal.makeWordSpacing(this.foText.getWordSpacing(), ls, this.spaceFont);
+        SpaceVal letterSpacing = SpaceVal.makeLetterSpacing(foText.getLetterSpacing());
+        SpaceVal wordSpacing = SpaceVal.makeWordSpacing(foText.getWordSpacing(), letterSpacing,
+                spaceFont);
 
         // letter space applies only to consecutive non-space characters,
         // while word space applies to space characters;
@@ -233,12 +239,9 @@
         // set letter space and word space dimension;
         // the default value "normal" was converted into a MinOptMax value
         // in the SpaceVal.makeWordSpacing() method
-        this.letterSpaceIPD = ls.getSpace();
-        this.wordSpaceIPD = MinOptMax.add(new MinOptMax(this.spaceCharIPD), this.ws.getSpace());
-
-        this.keepTogether = this.foText.getKeepTogether().getWithinLine()
-                .getEnum() == Constants.EN_ALWAYS;
-
+        letterSpaceIPD = letterSpacing.getSpace();
+        wordSpaceIPD = MinOptMax.getInstance(spaceCharIPD).plus(wordSpacing.getSpace());
+        keepTogether = foText.getKeepTogether().getWithinLine().getEnum() == Constants.EN_ALWAYS;
     }
 
     /**
@@ -254,111 +257,103 @@
     public void addAreas(final PositionIterator posIter, final LayoutContext context) {
 
         // Add word areas
-        AreaInfo ai;
+        AreaInfo areaInfo;
         int wordSpaceCount = 0;
         int letterSpaceCount = 0;
         int firstAreaInfoIndex = -1;
         int lastAreaInfoIndex = 0;
-        MinOptMax realWidth = new MinOptMax(0);
+        MinOptMax realWidth = MinOptMax.ZERO;
 
         /* On first area created, add any leading space.
          * Calculate word-space stretch value.
          */
-        AreaInfo lastAi = null;
+        AreaInfo lastAreaInfo = null;
         while (posIter.hasNext()) {
             final LeafPosition tbpNext = (LeafPosition) posIter.next();
             if (tbpNext == null) {
                 continue; //Ignore elements without Positions
             }
             if (tbpNext.getLeafPos() != -1) {
-                ai = (AreaInfo) this.vecAreaInfo.get(tbpNext.getLeafPos());
-                if (lastAi == null || ai.font != lastAi.font) {
-                    if (lastAi != null) {
-                        this.addAreaInfoAreas(lastAi, wordSpaceCount,
+                areaInfo = (AreaInfo) areaInfos.get(tbpNext.getLeafPos());
+                if (lastAreaInfo == null || areaInfo.font != lastAreaInfo.font) {
+                    if (lastAreaInfo != null) {
+                        addAreaInfoAreas(lastAreaInfo, wordSpaceCount,
                                 letterSpaceCount, firstAreaInfoIndex,
                                 lastAreaInfoIndex, realWidth, context);
                     }
                     firstAreaInfoIndex = tbpNext.getLeafPos();
                     wordSpaceCount = 0;
                     letterSpaceCount = 0;
-                    realWidth = new MinOptMax(0);
+                    realWidth = MinOptMax.ZERO;
                 }
-                wordSpaceCount += ai.wordSpaceCount;
-                letterSpaceCount += ai.letterSpaceCount;
-                realWidth.add(ai.areaIPD);
+                wordSpaceCount += areaInfo.wordSpaceCount;
+                letterSpaceCount += areaInfo.letterSpaceCount;
+                realWidth = realWidth.plus(areaInfo.areaIPD);
                 lastAreaInfoIndex = tbpNext.getLeafPos();
-                lastAi = ai;
+                lastAreaInfo = areaInfo;
             }
         }
-        if (lastAi != null) {
-            this.addAreaInfoAreas(lastAi, wordSpaceCount, letterSpaceCount,
-                    firstAreaInfoIndex, lastAreaInfoIndex, realWidth, context);
+        if (lastAreaInfo != null) {
+            addAreaInfoAreas(lastAreaInfo, wordSpaceCount, letterSpaceCount, firstAreaInfoIndex,
+                    lastAreaInfoIndex, realWidth, context);
         }
     }
 
-    private void addAreaInfoAreas(final AreaInfo ai, final int wordSpaceCount,
-            int letterSpaceCount, final int firstAreaInfoIndex,
-            final int lastAreaInfoIndex, final MinOptMax realWidth, final LayoutContext context) {
+    private void addAreaInfoAreas(AreaInfo areaInfo, int wordSpaceCount, int letterSpaceCount,
+                                  int firstAreaInfoIndex, int lastAreaInfoIndex,
+                                  MinOptMax realWidth, LayoutContext context) {
 
         // TODO: These two statements (if, for) were like this before my recent
         // changes. However, it seems as if they should use the AreaInfo from
-        // firstAreaInfoIndex.. lastAreaInfoIndex rather than just the last ai.
+        // firstAreaInfoIndex.. lastAreaInfoIndex rather than just the last areaInfo.
         // This needs to be checked.
-        final int textLength = ai.breakIndex - ai.startIndex;
-        if (ai.letterSpaceCount == textLength && !ai.isHyphenated
-                   && context.isLastArea()) {
+        int textLength = areaInfo.getCharLength();
+        if (areaInfo.letterSpaceCount == textLength && !areaInfo.isHyphenated
+                && context.isLastArea()) {
             // the line ends at a character like "/" or "-";
             // remove the letter space after the last character
-            realWidth.add(MinOptMax.multiply(this.letterSpaceIPD, -1));
+            realWidth = realWidth.minus(letterSpaceIPD);
             letterSpaceCount--;
         }
 
-        for (int i = ai.startIndex; i < ai.breakIndex; i++) {
-            final MinOptMax ladj = this.letterAdjustArray[i + 1];
-            if (ladj != null && ladj.isElastic()) {
+        for (int i = areaInfo.startIndex; i < areaInfo.breakIndex; i++) {
+            MinOptMax letterAdjustment = letterAdjustArray[i + 1];
+            if (letterAdjustment != null && letterAdjustment.isElastic()) {
                 letterSpaceCount++;
             }
         }
 
         // add hyphenation character if the last word is hyphenated
-        if (context.isLastArea() && ai.isHyphenated) {
-            realWidth.add(new MinOptMax(this.hyphIPD));
+        if (context.isLastArea() && areaInfo.isHyphenated) {
+            realWidth = realWidth.plus(hyphIPD);
         }
 
-        // Calculate adjustments
-        int difference = 0;
-        int totalAdjust = 0;
-        int wordSpaceDim = this.wordSpaceIPD.opt;
-        int letterSpaceDim = this.letterSpaceIPD.opt;
-        final double ipdAdjust = context.getIPDAdjust();
+        /* Calculate adjustments */
+        double ipdAdjust = context.getIPDAdjust();
 
         // calculate total difference between real and available width
+        int difference;
         if (ipdAdjust > 0.0) {
-            difference = (int) ((realWidth.max - realWidth.opt)
-                                * ipdAdjust);
+            difference = (int) (realWidth.getStretch() * ipdAdjust);
         } else {
-            difference = (int) ((realWidth.opt - realWidth.min)
-                                * ipdAdjust);
+            difference = (int) (realWidth.getShrink() * ipdAdjust);
         }
 
         // set letter space adjustment
+        int letterSpaceDim = letterSpaceIPD.getOpt();
         if (ipdAdjust > 0.0) {
-            letterSpaceDim
-                += (int) ((this.letterSpaceIPD.max - this.letterSpaceIPD.opt)
-                         * ipdAdjust);
-        } else  {
-            letterSpaceDim
-                += (int) ((this.letterSpaceIPD.opt - this.letterSpaceIPD.min)
-                         * ipdAdjust);
+            letterSpaceDim += (int) (letterSpaceIPD.getStretch() * ipdAdjust);
+        } else {
+            letterSpaceDim += (int) (letterSpaceIPD.getShrink() * ipdAdjust);
         }
-        totalAdjust += (letterSpaceDim - this.letterSpaceIPD.opt) * letterSpaceCount;
+        int totalAdjust = (letterSpaceDim - letterSpaceIPD.getOpt()) * letterSpaceCount;
 
         // set word space adjustment
-        //
+        int wordSpaceDim = wordSpaceIPD.getOpt();
         if (wordSpaceCount > 0) {
             wordSpaceDim += (difference - totalAdjust) / wordSpaceCount;
         }
-        totalAdjust += (wordSpaceDim - this.wordSpaceIPD.opt) * wordSpaceCount;
+        totalAdjust += (wordSpaceDim - wordSpaceIPD.getOpt()) * wordSpaceCount;
         if (totalAdjust != difference) {
             // the applied adjustment is greater or smaller than the needed one
             TextLayoutManager.LOG
@@ -370,9 +365,8 @@
             totalAdjust = difference;
         }
 
-        final TextArea t = this.createTextArea(realWidth, totalAdjust, context,
-                this.wordSpaceIPD.opt - this.spaceCharIPD, firstAreaInfoIndex,
-                lastAreaInfoIndex, context.isLastArea(), ai.font);
+        TextArea textArea = new TextAreaBuilder(realWidth, totalAdjust, context, firstAreaInfoIndex,
+                lastAreaInfoIndex, context.isLastArea(), areaInfo.font).build();
 
         // wordSpaceDim is computed in relation to wordSpaceIPD.opt
         // but the renderer needs to know the adjustment in relation
@@ -386,133 +380,205 @@
         //     = spaceCharIPD + letterSpaceAdjust +
         //       + (wordSpaceDim - spaceCharIPD -  2 * letterSpaceAdjust)
         //     = wordSpaceDim - letterSpaceAdjust
-        t.setTextLetterSpaceAdjust(letterSpaceDim);
-        t.setTextWordSpaceAdjust(wordSpaceDim - this.spaceCharIPD
-                                 - 2 * t.getTextLetterSpaceAdjust());
+        textArea.setTextLetterSpaceAdjust(letterSpaceDim);
+        textArea.setTextWordSpaceAdjust(wordSpaceDim - spaceCharIPD
+                - 2 * textArea.getTextLetterSpaceAdjust());
         if (context.getIPDAdjust() != 0) {
             // add information about space width
-            t.setSpaceDifference(this.wordSpaceIPD.opt - this.spaceCharIPD
-                                 - 2 * t.getTextLetterSpaceAdjust());
+            textArea.setSpaceDifference(wordSpaceIPD.getOpt() - spaceCharIPD
+                    - 2 * textArea.getTextLetterSpaceAdjust());
         }
-        this.parentLM.addChildArea(t);
+        parentLayoutManager.addChildArea(textArea);
     }
 
-    /**
-     * Create an inline word area.
-     * This creates a TextArea and sets up the various attributes.
-     *
-     * @param width the MinOptMax width of the content
-     * @param adjust the total ipd adjustment with respect to the optimal width
-     * @param context the layout context
-     * @param spaceDiff unused
-     * @param firstIndex the index of the first AreaInfo used for the TextArea
-     * @param lastIndex the index of the last AreaInfo used for the TextArea
-     * @param isLastArea is this TextArea the last in a line?
-     * @param font Font to be used in this particular TextArea
-     * @return the new text area
-     */
-    protected TextArea createTextArea(final MinOptMax width, final int adjust,
-            final LayoutContext context, final int spaceDiff, final int firstIndex,
-            final int lastIndex, final boolean isLastArea, final Font font) {
-        TextArea textArea;
-        if (context.getIPDAdjust() == 0.0) {
-            // create just a TextArea
-            textArea = new TextArea();
-        } else {
-            // justified area: create a TextArea with extra info
-            // about potential adjustments
-            textArea = new TextArea(width.max - width.opt,
-                                    width.opt - width.min,
-                                    adjust);
-        }
-        textArea.setIPD(width.opt + adjust);
-        textArea.setBPD(font.getAscender() - font.getDescender());
-
-        textArea.setBaselineOffset(font.getAscender());
-        if (textArea.getBPD() == this.alignmentContext.getHeight()) {
-            textArea.setOffset(0);
-        } else {
-            textArea.setOffset(this.alignmentContext.getOffset());
+    private final class TextAreaBuilder {
+
+        private final MinOptMax width;
+        private final int adjust;
+        private final LayoutContext context;
+        private final int firstIndex;
+        private final int lastIndex;
+        private final boolean isLastArea;
+        private final Font font;
+
+        private int blockProgressionDimension;
+        private AreaInfo areaInfo;
+        private StringBuffer wordChars;
+        private int[] letterAdjust;
+        private int letterAdjustIndex;
+
+        private TextArea textArea;
+
+        /**
+         * Creates a new <code>TextAreaBuilder</code> which itself builds an inline word area. This
+         * creates a TextArea and sets up the various attributes.
+         *
+         * @param width      the MinOptMax width of the content
+         * @param adjust     the total ipd adjustment with respect to the optimal width
+         * @param context    the layout context
+         * @param firstIndex the index of the first AreaInfo used for the TextArea
+         * @param lastIndex  the index of the last AreaInfo used for the TextArea
+         * @param isLastArea is this TextArea the last in a line?
+         * @param font       Font to be used in this particular TextArea
+         */
+        private TextAreaBuilder(MinOptMax width, int adjust, LayoutContext context,
+                                int firstIndex, int lastIndex, boolean isLastArea, Font font) {
+            this.width = width;
+            this.adjust = adjust;
+            this.context = context;
+            this.firstIndex = firstIndex;
+            this.lastIndex = lastIndex;
+            this.isLastArea = isLastArea;
+            this.font = font;
         }
 
-        // set the text of the TextArea, split into words and spaces
-        int wordStartIndex = -1;
-        AreaInfo areaInfo;
-        int len = 0;
-        for (int i = firstIndex; i <= lastIndex; i++) {
-            areaInfo = (AreaInfo) this.vecAreaInfo.get(i);
-            if (areaInfo.isSpace) {
-                // areaInfo stores information about spaces
-                // add the spaces - except zero-width spaces - to the TextArea
-                for (int j = areaInfo.startIndex; j < areaInfo.breakIndex; j++) {
-                    final char spaceChar = this.foText.charAt(j);
-                    if (!CharUtilities.isZeroWidthSpace(spaceChar)) {
-                        textArea.addSpace(spaceChar, 0,
-                                CharUtilities.isAdjustableSpace(spaceChar));
-                    }
-                }
+        private TextArea build() {
+            createTextArea();
+            setInlineProgressionDimension();
+            calcBlockProgressionDimension();
+            setBlockProgressionDimension();
+            setBaselineOffset();
+            setOffset();
+            setText();
+            TraitSetter.addFontTraits(textArea, font);
+            textArea.addTrait(Trait.COLOR, foText.getColor());
+            TraitSetter.addPtr(textArea, getPtr()); // used for accessibility
+            TraitSetter.addTextDecoration(textArea, foText.getTextDecoration());
+            TraitSetter.addFontTraits(textArea, font);
+            return textArea;
+        }
+
+        /**
+         * Creates an plain <code>TextArea</code> or a justified <code>TextArea</code> with
+         * additional information.
+         */
+        private void createTextArea() {
+            if (context.getIPDAdjust() == 0.0) {
+                textArea = new TextArea();
             } else {
-                // areaInfo stores information about a word fragment
-                if (wordStartIndex == -1) {
-                    // here starts a new word
-                    wordStartIndex = i;
-                    len = 0;
-                }
-                len += areaInfo.breakIndex - areaInfo.startIndex;
-                if (i == lastIndex || ((AreaInfo) this.vecAreaInfo.get(i + 1)).isSpace) {
-                    // here ends a new word
-                    // add a word to the TextArea
-                    if (isLastArea
-                        && i == lastIndex
-                        && areaInfo.isHyphenated) {
-                        len++;
-                    }
-                    final StringBuffer wordChars = new StringBuffer(len);
-                    final int[] letterAdjust = new int[len];
-                    int letter = 0;
-                    for (int j = wordStartIndex; j <= i; j++) {
-                        final AreaInfo ai = (AreaInfo) this.vecAreaInfo.get(j);
-                        int lsCount = ai.letterSpaceCount;
-                        /* TODO: in Java 5, StringBuffer has an append() variant
-                         * for CharSequence, so the below iteration can be replaced
-                         * by:
-                         *  wordChars.append(this.foText, ai.startIndex,
-                         *       ai.breakIndex - ai.startIndex);
-                         */
-                        for (int ci = ai.startIndex; ci < ai.breakIndex; ci++) {
-                            wordChars.append(this.foText.charAt(ci));
-                        }
-                        for (int k = 0; k < ai.breakIndex - ai.startIndex; k++) {
-                            final MinOptMax adj = this.letterAdjustArray[ai.startIndex + k];
-                            if (letter > 0) {
-                                letterAdjust[letter] = adj == null ? 0
-                                        : adj.opt;
-                            }
-                            if (lsCount > 0) {
-                                letterAdjust[letter] += textArea.getTextLetterSpaceAdjust();
-                                lsCount--;
-                            }
-                            letter++;
-                        }
+                textArea = new TextArea(width.getStretch(), width.getShrink(),
+                        adjust);
+            }
+        }
+
+        private void setInlineProgressionDimension() {
+            textArea.setIPD(width.getOpt() + adjust);
+        }
+
+        private void calcBlockProgressionDimension() {
+            blockProgressionDimension = font.getAscender() - font.getDescender();
+        }
+
+        private void setBlockProgressionDimension() {
+            textArea.setBPD(blockProgressionDimension);
+        }
+
+        private void setBaselineOffset() {
+            textArea.setBaselineOffset(font.getAscender());
+        }
+
+        private void setOffset() {
+            if (blockProgressionDimension == alignmentContext.getHeight()) {
+                textArea.setOffset(0);
+            } else {
+                textArea.setOffset(alignmentContext.getOffset());
+            }
+        }
+
+        /**
+         * Sets the text of the TextArea, split into words and spaces.
+         */
+        private void setText() {
+            int wordStartIndex = -1;
+            int wordCharLength = 0;
+            for (int wordIndex = firstIndex; wordIndex <= lastIndex; wordIndex++) {
+                areaInfo = getAreaInfo(wordIndex);
+                if (areaInfo.isSpace) {
+                    addSpaces();
+                } else {
+                    // areaInfo stores information about a word fragment
+                    if (wordStartIndex == -1) {
+                        // here starts a new word
+                        wordStartIndex = wordIndex;
+                        wordCharLength = 0;
                     }
-                    // String wordChars = new String(textArray, wordStartIndex, len);
-                    if (isLastArea
-                        && i == lastIndex
-                        && areaInfo.isHyphenated) {
-                        // add the hyphenation character
-                        wordChars.append(this.foText.getCommonHyphenation().getHyphChar(font));
+                    wordCharLength += areaInfo.getCharLength();
+                    if (isWordEnd(wordIndex)) {
+                        addWord(wordStartIndex, wordIndex, wordCharLength);
+                        wordStartIndex = -1;
                     }
-                    textArea.addWord(wordChars.toString(), 0, letterAdjust);
-                    wordStartIndex = -1;
                 }
             }
         }
-        TraitSetter.addFontTraits(textArea, font);
-        textArea.addTrait(Trait.COLOR, this.foText.getColor());
-        TraitSetter.addPtr(textArea, getPtr()); // used for accessibility
-        TraitSetter.addTextDecoration(textArea, this.foText.getTextDecoration());
 
-        return textArea;
+        private boolean isWordEnd(int areaInfoIndex) {
+            return areaInfoIndex == lastIndex || getAreaInfo(areaInfoIndex + 1).isSpace;
+        }
+
+        private void addWord(int startIndex, int endIndex, int charLength) {
+            if (isHyphenated(endIndex)) {
+                charLength++;
+            }
+            initWord(charLength);
+            for (int i = startIndex; i <= endIndex; i++) {
+                AreaInfo wordAreaInfo = getAreaInfo(i);
+                addWordChars(wordAreaInfo);
+                addLetterAdjust(wordAreaInfo);
+            }
+            if (isHyphenated(endIndex)) {
+                addHyphenationChar();
+            }
+            textArea.addWord(wordChars.toString(), 0, letterAdjust);
+        }
+
+        private void initWord(int charLength) {
+            wordChars = new StringBuffer(charLength);
+            letterAdjust = new int[charLength];
+            letterAdjustIndex = 0;
+        }
+
+        private boolean isHyphenated(int endIndex) {
+            return isLastArea && endIndex == lastIndex && areaInfo.isHyphenated;
+        }
+
+        private void addHyphenationChar() {
+            wordChars.append(foText.getCommonHyphenation().getHyphChar(font));
+        }
+
+        private void addWordChars(AreaInfo wordAreaInfo) {
+            for (int i = wordAreaInfo.startIndex; i < wordAreaInfo.breakIndex; i++) {
+                wordChars.append(foText.charAt(i));
+            }
+        }
+
+        private void addLetterAdjust(AreaInfo wordAreaInfo) {
+            int letterSpaceCount = wordAreaInfo.letterSpaceCount;
+            for (int i = wordAreaInfo.startIndex; i < wordAreaInfo.breakIndex; i++) {
+                if (letterAdjustIndex > 0) {
+                    MinOptMax adj = letterAdjustArray[i];
+                    letterAdjust[letterAdjustIndex] = adj == null ? 0 : adj.getOpt();
+                }
+                if (letterSpaceCount > 0) {
+                    letterAdjust[letterAdjustIndex] += textArea.getTextLetterSpaceAdjust();
+                    letterSpaceCount--;
+                }
+                letterAdjustIndex++;
+            }
+        }
+
+        /**
+         * The <code>AreaInfo</code> stores information about spaces.
+         * <p/>
+         * Add the spaces - except zero-width spaces - to the TextArea.
+         */
+        private void addSpaces() {
+            for (int i = areaInfo.startIndex; i < areaInfo.breakIndex; i++) {
+                char spaceChar = foText.charAt(i);
+                if (!CharUtilities.isZeroWidthSpace(spaceChar)) {
+                    textArea.addSpace(spaceChar, 0, CharUtilities.isAdjustableSpace(spaceChar));
+                }
+            }
+        }
     }
 
     /**
@@ -520,7 +586,7 @@
      * @return ptr of fobj
      */
     private String getPtr() {
-        FObj fobj = this.parentLM.getFObj();
+        FObj fobj = parentLayoutManager.getFObj();
         if (fobj instanceof StructurePointerPropertySet) {
             return (((StructurePointerPropertySet) fobj).getPtr());
         } else {
@@ -529,11 +595,15 @@
         }
     }
 
-    private void addToLetterAdjust(final int index, final int width) {
-        if (this.letterAdjustArray[index] == null) {
-            this.letterAdjustArray[index] = new MinOptMax(width);
+    private AreaInfo getAreaInfo(int index) {
+        return (AreaInfo) areaInfos.get(index);
+    }
+
+    private void addToLetterAdjust(int index, int width) {
+        if (letterAdjustArray[index] == null) {
+            letterAdjustArray[index] = MinOptMax.getInstance(width);
         } else {
-            this.letterAdjustArray[index].add(width);
+            letterAdjustArray[index] = letterAdjustArray[index].plus(width);
         }
     }
 
@@ -544,32 +614,33 @@
      */
     private static boolean isSpace(final char ch) {
         return ch == CharUtilities.SPACE
-            || CharUtilities.isNonBreakableSpace(ch)
-            || CharUtilities.isFixedWidthSpace(ch);
+                || CharUtilities.isNonBreakableSpace(ch)
+                || CharUtilities.isFixedWidthSpace(ch);
     }
 
     /** {@inheritDoc} */
     public List getNextKnuthElements(final LayoutContext context, final int alignment) {
-        this.lineStartBAP = context.getLineStartBorderAndPaddingWidth();
-        this.lineEndBAP = context.getLineEndBorderAndPaddingWidth();
-        this.alignmentContext = context.getAlignmentContext();
+        lineStartBAP = context.getLineStartBorderAndPaddingWidth();
+        lineEndBAP = context.getLineEndBorderAndPaddingWidth();
+        alignmentContext = context.getAlignmentContext();
 
         final List returnList = new LinkedList();
         KnuthSequence sequence = new InlineKnuthSequence();
-        AreaInfo ai = null;
-        AreaInfo prevAi = null;
+        AreaInfo areaInfo = null;
+        AreaInfo prevAreaInfo = null;
         returnList.add(sequence);
 
-        final LineBreakStatus lbs = new LineBreakStatus();
-        this.thisStart = this.nextStart;
+        LineBreakStatus lineBreakStatus = new LineBreakStatus();
+        thisStart = nextStart;
         boolean inWord = false;
         boolean inWhitespace = false;
         char ch = 0;
-        while (this.nextStart < this.foText.length()) {
-            ch = this.foText.charAt(this.nextStart);
+        while (nextStart < foText.length()) {
+            ch = foText.charAt(nextStart);
             boolean breakOpportunity = false;
-            final byte breakAction = this.keepTogether ? LineBreakStatus.PROHIBITED_BREAK
-                    : lbs.nextChar(ch);
+            byte breakAction = keepTogether
+                    ? LineBreakStatus.PROHIBITED_BREAK
+                    : lineBreakStatus.nextChar(ch);
             switch (breakAction) {
                 case LineBreakStatus.COMBINING_PROHIBITED_BREAK:
                 case LineBreakStatus.PROHIBITED_BREAK:
@@ -589,62 +660,58 @@
                         || TextLayoutManager.isSpace(ch)
                         || CharUtilities.isExplicitBreak(ch)) {
                     // this.foText.charAt(lastIndex) == CharUtilities.SOFT_HYPHEN
-                    prevAi = this.processWord(alignment, sequence, prevAi, ch,
+                    prevAreaInfo = processWord(alignment, sequence, prevAreaInfo, ch,
                             breakOpportunity, true);
                 }
             } else if (inWhitespace) {
                 if (ch != CharUtilities.SPACE || breakOpportunity) {
-                    prevAi = this.processWhitespace(alignment, sequence,
-                            breakOpportunity);
+                    prevAreaInfo = processWhitespace(alignment, sequence, breakOpportunity);
                 }
             } else {
-                if (ai != null) {
-                    prevAi = ai;
-                    ai = this.processLeftoverAi(alignment, sequence, ai, ch,
+                if (areaInfo != null) {
+                    prevAreaInfo = areaInfo;
+                    processLeftoverAreaInfo(alignment, sequence, areaInfo,
                             ch == CharUtilities.SPACE || breakOpportunity);
+                    areaInfo = null;
                 }
                 if (breakAction == LineBreakStatus.EXPLICIT_BREAK) {
-                    sequence = this.processLinebreak(returnList, sequence);
+                    sequence = processLinebreak(returnList, sequence);
                 }
             }
 
             if (ch == CharUtilities.SPACE
-                    && this.foText.getWhitespaceTreatment() == Constants.EN_PRESERVE
+                    && foText.getWhitespaceTreatment() == Constants.EN_PRESERVE
                     || ch == CharUtilities.NBSPACE) {
                 // preserved space or non-breaking space:
                 // create the AreaInfo object
-                ai = new AreaInfo(this.nextStart, this.nextStart + 1,
-                        1, 0, this.wordSpaceIPD, false, true,
-                        breakOpportunity, this.spaceFont);
-                this.thisStart = this.nextStart + 1;
+                areaInfo = new AreaInfo(nextStart, nextStart + 1, 1, 0, wordSpaceIPD, false, true,
+                        breakOpportunity, spaceFont);
+                thisStart = nextStart + 1;
             } else if (CharUtilities.isFixedWidthSpace(ch) || CharUtilities.isZeroWidthSpace(ch)) {
                 // create the AreaInfo object
-                final Font font = FontSelector.selectFontForCharacterInText(ch,
-                        this.foText, this);
-                final MinOptMax ipd = new MinOptMax(font.getCharWidth(ch));
-                ai = new AreaInfo(this.nextStart, this.nextStart + 1,
-                        0, 0, ipd, false, true,
+                Font font = FontSelector.selectFontForCharacterInText(ch, foText, this);
+                MinOptMax ipd = MinOptMax.getInstance(font.getCharWidth(ch));
+                areaInfo = new AreaInfo(nextStart, nextStart + 1, 0, 0, ipd, false, true,
                         breakOpportunity, font);
-                this.thisStart = this.nextStart + 1;
+                thisStart = nextStart + 1;
             } else if (CharUtilities.isExplicitBreak(ch)) {
                 //mandatory break-character: only advance index
-                this.thisStart = this.nextStart + 1;
+                thisStart = nextStart + 1;
             }
 
-            inWord = !TextLayoutManager.isSpace(ch)
-                    && !CharUtilities.isExplicitBreak(ch);
+            inWord = !TextLayoutManager.isSpace(ch) && !CharUtilities.isExplicitBreak(ch);
             inWhitespace = ch == CharUtilities.SPACE
-                    && this.foText.getWhitespaceTreatment() != Constants.EN_PRESERVE;
-            this.nextStart++;
-        } // end of while
+                    && foText.getWhitespaceTreatment() != Constants.EN_PRESERVE;
+            nextStart++;
+        }
 
         // Process any last elements
         if (inWord) {
-            this.processWord(alignment, sequence, prevAi, ch, false, false);
+            processWord(alignment, sequence, prevAreaInfo, ch, false, false);
         } else if (inWhitespace) {
-            this.processWhitespace(alignment, sequence, true);
-        } else if (ai != null) {
-            this.processLeftoverAi(alignment, sequence, ai, ch,
+            processWhitespace(alignment, sequence, true);
+        } else if (areaInfo != null) {
+            processLeftoverAreaInfo(alignment, sequence, areaInfo,
                     ch == CharUtilities.ZERO_WIDTH_SPACE);
         } else if (CharUtilities.isExplicitBreak(ch)) {
             this.processLinebreak(returnList, sequence);
@@ -655,7 +722,7 @@
             ListUtil.removeLast(returnList);
         }
 
-        this.setFinished(true);
+        setFinished(true);
         if (returnList.isEmpty()) {
             return null;
         } else {
@@ -663,12 +730,9 @@
         }
     }
 
-    private KnuthSequence processLinebreak(final List returnList,
-            KnuthSequence sequence) {
-        if (this.lineEndBAP != 0) {
-            sequence.add(
-                    new KnuthGlue(this.lineEndBAP, 0, 0,
-                                  this.auxiliaryPosition, true));
+    private KnuthSequence processLinebreak(List returnList, KnuthSequence sequence) {
+        if (lineEndBAP != 0) {
+            sequence.add(new KnuthGlue(lineEndBAP, 0, 0, auxiliaryPosition, true));
         }
         sequence.endSequence();
         sequence = new InlineKnuthSequence();
@@ -676,32 +740,29 @@
         return sequence;
     }
 
-    private AreaInfo processLeftoverAi(final int alignment,
-            final KnuthSequence sequence, AreaInfo ai, final char ch,
-            final boolean breakOpportunityAfter) {
-        this.vecAreaInfo.add(ai);
-        ai.breakOppAfter = breakOpportunityAfter;
-        this.addElementsForASpace(sequence, alignment, ai, this.vecAreaInfo.size() - 1);
-        ai = null;
-        return ai;
+    private void processLeftoverAreaInfo(int alignment,
+                                         KnuthSequence sequence, AreaInfo areaInfo,
+                                         boolean breakOpportunityAfter) {
+        areaInfos.add(areaInfo);
+        areaInfo.breakOppAfter = breakOpportunityAfter;
+        addElementsForASpace(sequence, alignment, areaInfo, areaInfos.size() - 1);
     }
 
     private AreaInfo processWhitespace(final int alignment,
             final KnuthSequence sequence, final boolean breakOpportunity) {
         // End of whitespace
         // create the AreaInfo object
-        AreaInfo ai = new AreaInfo(this.thisStart, this.nextStart,
-                this.nextStart - this.thisStart, 0,
-                MinOptMax.multiply(this.wordSpaceIPD, this.nextStart
-                        - this.thisStart), false, true,
-                breakOpportunity, this.spaceFont);
-        this.vecAreaInfo.add(ai);
+        assert nextStart >= thisStart;
+        AreaInfo areaInfo = new AreaInfo(thisStart, nextStart, nextStart - thisStart, 0,
+                wordSpaceIPD.mult(nextStart - thisStart), false, true, breakOpportunity, spaceFont);
+
+        areaInfos.add(areaInfo);
 
         // create the elements
-        this.addElementsForASpace(sequence, alignment, ai, this.vecAreaInfo.size() - 1);
+        addElementsForASpace(sequence, alignment, areaInfo, areaInfos.size() - 1);
 
-        this.thisStart = this.nextStart;
-        return ai;
+        thisStart = nextStart;
+        return areaInfo;
     }
 
     private AreaInfo processWord(final int alignment, final KnuthSequence sequence,
@@ -709,39 +770,36 @@
             final boolean checkEndsWithHyphen) {
 
         //Word boundary found, process widths and kerning
-        int lastIndex = this.nextStart;
-        while (lastIndex > 0
-                && foText.charAt(lastIndex - 1) == CharUtilities.SOFT_HYPHEN) {
+        int lastIndex = nextStart;
+        while (lastIndex > 0 && foText.charAt(lastIndex - 1) == CharUtilities.SOFT_HYPHEN) {
             lastIndex--;
         }
         final boolean endsWithHyphen = checkEndsWithHyphen
                 && foText.charAt(lastIndex) == CharUtilities.SOFT_HYPHEN;
-        final Font font = FontSelector
-                .selectFontForCharactersInText(foText,
-                        this.thisStart, lastIndex, foText, this);
-        final int wordLength = lastIndex - this.thisStart;
-        final boolean kerning = font.hasKerning();
-        final MinOptMax wordIPD = new MinOptMax(0);
-        for (int i = this.thisStart; i < lastIndex; i++) {
-            final char currentChar = foText.charAt(i);
+        Font font = FontSelector.selectFontForCharactersInText(foText, thisStart, lastIndex, foText, this);
+        int wordLength = lastIndex - thisStart;
+        boolean kerning = font.hasKerning();
+        MinOptMax wordIPD = MinOptMax.ZERO;
+        for (int i = thisStart; i < lastIndex; i++) {
+            char currentChar = foText.charAt(i);
 
             //character width
-            final int charWidth = font.getCharWidth(currentChar);
-            wordIPD.add(charWidth);
+            int charWidth = font.getCharWidth(currentChar);
+            wordIPD = wordIPD.plus(charWidth);
 
             //kerning
             if (kerning) {
                 int kern = 0;
-                if (i > this.thisStart) {
-                    final char previousChar = foText.charAt(i - 1);
+                if (i > thisStart) {
+                    char previousChar = foText.charAt(i - 1);
                     kern = font.getKernValue(previousChar, currentChar);
                 } else if (prevAreaInfo != null && !prevAreaInfo.isSpace && prevAreaInfo.breakIndex > 0) {
-                    final char previousChar = foText.charAt(prevAreaInfo.breakIndex - 1);
+                    char previousChar = foText.charAt(prevAreaInfo.breakIndex - 1);
                     kern = font.getKernValue(previousChar, currentChar);
                 }
                 if (kern != 0) {
-                    this.addToLetterAdjust(i, kern);
-                    wordIPD.add(kern);
+                    addToLetterAdjust(i, kern);
+                    wordIPD = wordIPD.plus(kern);
                 }
             }
         }
@@ -752,7 +810,7 @@
                 && endsWithHyphen) {
             final int kern = font.getKernValue(foText.charAt(lastIndex - 1), ch);
             if (kern != 0) {
-                this.addToLetterAdjust(lastIndex, kern);
+                addToLetterAdjust(lastIndex, kern);
                 //TODO: add kern to wordIPD?
             }
         }
@@ -763,21 +821,21 @@
         if (breakOpportunity && !TextLayoutManager.isSpace(ch)) {
             iLetterSpaces++;
         }
-        wordIPD.add(MinOptMax.multiply(this.letterSpaceIPD, iLetterSpaces));
+        assert iLetterSpaces >= 0;
+        wordIPD = wordIPD.plus(letterSpaceIPD.mult(iLetterSpaces));
 
         // create the AreaInfo object
-        AreaInfo areaInfo = new AreaInfo(this.thisStart, lastIndex, 0,
+        AreaInfo areaInfo = new AreaInfo(thisStart, lastIndex, 0,
                 iLetterSpaces, wordIPD,
                 endsWithHyphen,
                 false, breakOpportunity, font);
         prevAreaInfo = areaInfo;
-        this.vecAreaInfo.add(areaInfo);
-        this.tempStart = this.nextStart;
+        areaInfos.add(areaInfo);
+        tempStart = nextStart;
 
         //add the elements
-        this.addElementsForAWordFragment(sequence, alignment, areaInfo,
-                this.vecAreaInfo.size() - 1, this.letterSpaceIPD);
-        this.thisStart = this.nextStart;
+        addElementsForAWordFragment(sequence, alignment, areaInfo, areaInfos.size() - 1);
+        thisStart = nextStart;
 
         return prevAreaInfo;
     }
@@ -788,49 +846,39 @@
         // look at the Position stored in the first element in oldList
         // which is always a box
         ListIterator oldListIterator = oldList.listIterator();
-        final KnuthElement el = (KnuthElement)oldListIterator.next();
-        final LeafPosition pos = (LeafPosition) ((KnuthBox) el).getPosition();
-        final int idx = pos.getLeafPos();
+        KnuthElement knuthElement = (KnuthElement) oldListIterator.next();
+        LeafPosition pos = (LeafPosition) ((KnuthBox) knuthElement).getPosition();
+        int index = pos.getLeafPos();
         //element could refer to '-1' position, for non-collapsed spaces (?)
-        if (idx > -1) {
-            final AreaInfo ai = (AreaInfo) this.vecAreaInfo.get(idx);
-            ai.letterSpaceCount++;
-            ai.areaIPD.add(this.letterSpaceIPD);
-            if (TextLayoutManager.BREAK_CHARS.indexOf(this.foText.charAt(this.tempStart - 1)) >= 0) {
+        if (index > -1) {
+            AreaInfo areaInfo = getAreaInfo(index);
+            areaInfo.letterSpaceCount++;
+            areaInfo.addToAreaIPD(letterSpaceIPD);
+            if (TextLayoutManager.BREAK_CHARS.indexOf(foText.charAt(tempStart - 1)) >= 0) {
                 // the last character could be used as a line break
                 // append new elements to oldList
                 oldListIterator = oldList.listIterator(oldList.size());
                 oldListIterator.add(new KnuthPenalty(0, KnuthPenalty.FLAGGED_PENALTY, true,
-                                                     this.auxiliaryPosition, false));
-                oldListIterator.add(new KnuthGlue(this.letterSpaceIPD.opt,
-                                           this.letterSpaceIPD.max - this.letterSpaceIPD.opt,
-                                           this.letterSpaceIPD.opt - this.letterSpaceIPD.min,
-                                           this.auxiliaryPosition, false));
-            } else if (this.letterSpaceIPD.min == this.letterSpaceIPD.max) {
+                        auxiliaryPosition, false));
+                oldListIterator.add(new KnuthGlue(letterSpaceIPD, auxiliaryPosition, false));
+            } else if (letterSpaceIPD.isStiff()) {
                 // constant letter space: replace the box
-                oldListIterator.set(new KnuthInlineBox(ai.areaIPD.opt,
-                        this.alignmentContext, pos, false));
+                oldListIterator.set(new KnuthInlineBox(areaInfo.areaIPD.getOpt(),
+                        alignmentContext, pos, false));
             } else {
                 // adjustable letter space: replace the glue
                 oldListIterator.next(); // this would return the penalty element
                 oldListIterator.next(); // this would return the glue element
-                oldListIterator
-                        .set(new KnuthGlue(
-                                ai.letterSpaceCount * this.letterSpaceIPD.opt,
-                                ai.letterSpaceCount
-                                        * (this.letterSpaceIPD.max - this.letterSpaceIPD.opt),
-                                ai.letterSpaceCount
-                                        * (this.letterSpaceIPD.opt - this.letterSpaceIPD.min),
-                                this.auxiliaryPosition, true));
+                oldListIterator.set(new KnuthGlue(letterSpaceIPD.mult(areaInfo.letterSpaceCount),
+                        auxiliaryPosition, true));
             }
         }
         return oldList;
     }
 
     /**
-     * remove the AreaInfo object represented by the given elements,
-     * so that it won't generate any element when getChangedKnuthElements
-     * will be called
+     * Removes the <code>AreaInfo</code> object represented by the given elements, so that it won't
+     * generate any element when <code>getChangedKnuthElements</code> is called.
      *
      * @param oldList the elements representing the word space
      */
@@ -848,99 +896,91 @@
             oldListIterator.next();
             oldListIterator.next();
         }
-        final int leafValue = ((LeafPosition) ((KnuthElement) oldListIterator
-                .next()).getPosition()).getLeafPos();
+        KnuthElement knuthElement = (KnuthElement) oldListIterator.next();
+        int leafValue = ((LeafPosition) knuthElement.getPosition()).getLeafPos();
         // only the last word space can be a trailing space!
-        if (leafValue == this.vecAreaInfo.size() - 1) {
-            this.vecAreaInfo.remove(leafValue);
+        if (leafValue == areaInfos.size() - 1) {
+            areaInfos.remove(leafValue);
         } else {
             TextLayoutManager.LOG.error("trying to remove a non-trailing word space");
         }
     }
 
-    /** {@inheritDoc} */
-    public void hyphenate(final Position pos, final HyphContext hc) {
-        final AreaInfo ai
-            = (AreaInfo) this.vecAreaInfo.get(((LeafPosition) pos).getLeafPos());
-        int startIndex = ai.startIndex;
+    /**
+     * {@inheritDoc}
+     */
+    public void hyphenate(Position pos, HyphContext hyphContext) {
+        AreaInfo areaInfo = getAreaInfo(((LeafPosition) pos).getLeafPos());
+        int startIndex = areaInfo.startIndex;
         int stopIndex;
         boolean nothingChanged = true;
-        final Font font = ai.font;
+        Font font = areaInfo.font;
 
-        while (startIndex < ai.breakIndex) {
-            final MinOptMax newIPD = new MinOptMax(0);
+        while (startIndex < areaInfo.breakIndex) {
+            MinOptMax newIPD = MinOptMax.ZERO;
             boolean hyphenFollows;
 
-            stopIndex = startIndex + hc.getNextHyphPoint();
-            if (hc.hasMoreHyphPoints() && stopIndex <= ai.breakIndex) {
+            stopIndex = startIndex + hyphContext.getNextHyphPoint();
+            if (hyphContext.hasMoreHyphPoints() && stopIndex <= areaInfo.breakIndex) {
                 // stopIndex is the index of the first character
                 // after a hyphenation point
                 hyphenFollows = true;
             } else {
                 // there are no more hyphenation points,
-                // or the next one is after ai.breakIndex
+                // or the next one is after areaInfo.breakIndex
                 hyphenFollows = false;
-                stopIndex = ai.breakIndex;
+                stopIndex = areaInfo.breakIndex;
             }
 
-            hc.updateOffset(stopIndex - startIndex);
+            hyphContext.updateOffset(stopIndex - startIndex);
 
             //log.info("Word: " + new String(textArray, startIndex, stopIndex - startIndex));
             for (int i = startIndex; i < stopIndex; i++) {
-                final char c = this.foText.charAt(i);
-                newIPD.add(new MinOptMax(font.getCharWidth(c)));
+                char ch = foText.charAt(i);
+                newIPD = newIPD.plus(font.getCharWidth(ch));
                 //if (i > startIndex) {
                 if (i < stopIndex) {
-                    MinOptMax la = this.letterAdjustArray[i + 1];
+                    MinOptMax letterAdjust = letterAdjustArray[i + 1];
                     if (i == stopIndex - 1 && hyphenFollows) {
                         //the letter adjust here needs to be handled further down during
                         //element generation because it depends on hyph/no-hyph condition
-                        la = null;
+                        letterAdjust = null;
                     }
-                    if (la != null) {
-                        newIPD.add(la);
+                    if (letterAdjust != null) {
+                        newIPD = newIPD.plus(letterAdjust);
                     }
                 }
             }
+
             // add letter spaces
-            final boolean isWordEnd
-                = stopIndex == ai.breakIndex
-                && ai.letterSpaceCount < ai.breakIndex - ai.startIndex;
-            newIPD.add(MinOptMax.multiply(this.letterSpaceIPD,
-                                          (isWordEnd
-                                           ? stopIndex - startIndex - 1
-                                           : stopIndex - startIndex)));
-
-            if (!(nothingChanged
-                  && stopIndex == ai.breakIndex
-                  && !hyphenFollows)) {
+            boolean isWordEnd = stopIndex == areaInfo.breakIndex && areaInfo.letterSpaceCount < areaInfo.getCharLength();
+            int letterSpaceCount = isWordEnd ? stopIndex - startIndex - 1 : stopIndex - startIndex;
+
+            assert letterSpaceCount >= 0;
+            newIPD = newIPD.plus(letterSpaceIPD.mult(letterSpaceCount));
+
+            if (!(nothingChanged && stopIndex == areaInfo.breakIndex && !hyphenFollows)) {
                 // the new AreaInfo object is not equal to the old one
-                if (this.changeList == null) {
-                    this.changeList = new LinkedList();
-                }
-                this.changeList.add(new PendingChange(new AreaInfo(
-                        startIndex, stopIndex, 0,
-                        (isWordEnd ? stopIndex - startIndex - 1
-                                : stopIndex - startIndex), newIPD,
-                        hyphenFollows, false, false, font),
+                changeList.add(new PendingChange(new AreaInfo(startIndex, stopIndex, 0,
+                        letterSpaceCount, newIPD, hyphenFollows, false, false, font),
                         ((LeafPosition) pos).getLeafPos()));
                 nothingChanged = false;
             }
             startIndex = stopIndex;
         }
-        this.hasChanged = (this.hasChanged || !nothingChanged);
+        hasChanged |= !nothingChanged;
     }
 
     /** {@inheritDoc} */
     public boolean applyChanges(final List oldList) {
-        this.setFinished(false);
+        setFinished(false);
 
-        if (this.changeList != null && !this.changeList.isEmpty()) {
+        if (!changeList.isEmpty()) {
             int areaInfosAdded = 0;
             int areaInfosRemoved = 0;
             int oldIndex = -1, changeIndex;
             PendingChange currChange;
-            final ListIterator changeListIterator = this.changeList.listIterator();
+            ListIterator changeListIterator = changeList.listIterator();
             while (changeListIterator.hasNext()) {
                 currChange = (PendingChange) changeListIterator.next();
                 if (currChange.index == oldIndex) {
@@ -951,111 +991,109 @@
                     areaInfosAdded++;
                     oldIndex = currChange.index;
                     changeIndex = currChange.index + areaInfosAdded - areaInfosRemoved;
-                    this.vecAreaInfo.remove(changeIndex);
+                    areaInfos.remove(changeIndex);
                 }
-                this.vecAreaInfo.add(changeIndex, currChange.ai);
+                areaInfos.add(changeIndex, currChange.areaInfo);
             }
-            this.changeList.clear();
+            changeList.clear();
         }
 
-        this.returnedIndex = 0;
-        return this.hasChanged;
+        returnedIndex = 0;
+        return hasChanged;
     }
 
     /** {@inheritDoc} */
-    public List getChangedKnuthElements(final List oldList,
-                                              final int alignment) {
-        if (this.isFinished()) {
+    public List getChangedKnuthElements(final List oldList, final int alignment) {
+        if (isFinished()) {
             return null;
         }
 
         final LinkedList returnList = new LinkedList();
 
-        while (this.returnedIndex < this.vecAreaInfo.size()) {
-            final AreaInfo ai = (AreaInfo) this.vecAreaInfo.get(this.returnedIndex);
-            if (ai.wordSpaceCount == 0) {
-                // ai refers either to a word or a word fragment
-                this.addElementsForAWordFragment(returnList, alignment, ai,
-                        this.returnedIndex, this.letterSpaceIPD);
+        while (returnedIndex < areaInfos.size()) {
+            AreaInfo areaInfo = getAreaInfo(returnedIndex);
+            if (areaInfo.wordSpaceCount == 0) {
+                // areaInfo refers either to a word or a word fragment
+                addElementsForAWordFragment(returnList, alignment, areaInfo, returnedIndex);
             } else {
-                // ai refers to a space
-                this.addElementsForASpace(returnList, alignment, ai, this.returnedIndex);
+                // areaInfo refers to a space
+                addElementsForASpace(returnList, alignment, areaInfo, returnedIndex);
             }
-            this.returnedIndex++;
-        } // end of while
-        this.setFinished(true);
+            returnedIndex++;
+        }
+        setFinished(true);
         //ElementListObserver.observe(returnList, "text-changed", null);
         return returnList;
     }
 
-    /** {@inheritDoc} */
-    public void getWordChars(final StringBuffer sbChars, final Position pos) {
-        final int leafValue = ((LeafPosition) pos).getLeafPos();
+    /**
+     * {@inheritDoc}
+     */
+    public String getWordChars(Position pos) {
+        int leafValue = ((LeafPosition) pos).getLeafPos();
         if (leafValue != -1) {
-            final AreaInfo ai = (AreaInfo) this.vecAreaInfo.get(leafValue);
-            for (int i = ai.startIndex; i < ai.breakIndex; ++i) {
-                sbChars.append(this.foText.charAt(i));
+            AreaInfo areaInfo = getAreaInfo(leafValue);
+            StringBuffer buffer = new StringBuffer(areaInfo.getCharLength());
+            for (int i = areaInfo.startIndex; i < areaInfo.breakIndex; i++) {
+                buffer.append(foText.charAt(i));
             }
+            return buffer.toString();
+        } else {
+            return "";
         }
     }
 
-    private void addElementsForASpace(final List baseList,
-                                      final int alignment,
-                                      final AreaInfo ai,
-                                      final int leafValue) {
-        final LeafPosition mainPosition = new LeafPosition(this, leafValue);
+    private void addElementsForASpace(List baseList, int alignment, AreaInfo areaInfo,
+                                      int leafValue) {
+        LeafPosition mainPosition = new LeafPosition(this, leafValue);
 
-        if (!ai.breakOppAfter) {
+        if (!areaInfo.breakOppAfter) {
             // a non-breaking space
             if (alignment == Constants.EN_JUSTIFY) {
                 // the space can stretch and shrink, and must be preserved
                 // when starting a line
-                baseList.add(this.makeAuxiliaryZeroWidthBox());
-                baseList.add(this.makeZeroWidthPenalty(KnuthElement.INFINITE));
-                baseList.add(new KnuthGlue(ai.areaIPD.opt, ai.areaIPD.max - ai.areaIPD.opt,
-                        ai.areaIPD.opt - ai.areaIPD.min, mainPosition, false));
+                baseList.add(makeAuxiliaryZeroWidthBox());
+                baseList.add(makeZeroWidthPenalty(KnuthElement.INFINITE));
+                baseList.add(new KnuthGlue(areaInfo.areaIPD, mainPosition, false));
             } else {
                 // the space does not need to stretch or shrink, and must be
                 // preserved when starting a line
-                baseList.add(new KnuthInlineBox(ai.areaIPD.opt, null,
-                        mainPosition, true));
+                baseList.add(new KnuthInlineBox(areaInfo.areaIPD.getOpt(), null, mainPosition,
+                        true));
             }
         } else {
-            if (this.foText.charAt(ai.startIndex) != CharUtilities.SPACE
-                    || this.foText.getWhitespaceTreatment() == Constants.EN_PRESERVE) {
+            if (foText.charAt(areaInfo.startIndex) != CharUtilities.SPACE
+                    || foText.getWhitespaceTreatment() == Constants.EN_PRESERVE) {
                 // a breaking space that needs to be preserved
-                this.addElementsForBreakingSpace(baseList, alignment, ai,
-                                this.auxiliaryPosition, 0, mainPosition,
-                                ai.areaIPD.opt, true);
+                baseList.addAll(getElementsForBreakingSpace(alignment, areaInfo, auxiliaryPosition, 0,
+                        mainPosition, areaInfo.areaIPD.getOpt(), true));
             } else {
                 // a (possible block) of breaking spaces
-                this.addElementsForBreakingSpace(baseList, alignment, ai,
-                                mainPosition, ai.areaIPD.opt,
-                                this.auxiliaryPosition, 0, false);
+                baseList.addAll(getElementsForBreakingSpace(alignment, areaInfo, mainPosition,
+                        areaInfo.areaIPD.getOpt(), auxiliaryPosition, 0, false));
             }
         }
     }
 
-    private void addElementsForBreakingSpace(final List baseList,
-            final int alignment, final AreaInfo ai, final Position pos2,
-            final int p2WidthOffset, final Position pos3,
-            final int p3WidthOffset, final boolean skipZeroCheck) {
+    private List getElementsForBreakingSpace(int alignment, AreaInfo areaInfo, Position pos2,
+                                             int p2WidthOffset, Position pos3,
+                                             int p3WidthOffset, boolean skipZeroCheck) {
+        List elements = new ArrayList();
+
         switch (alignment) {
         case EN_CENTER:
             // centered text:
             // if the second element is chosen as a line break these elements
             // add a constant amount of stretch at the end of a line and at the
             // beginning of the next one, otherwise they don't add any stretch
-            baseList.add(new KnuthGlue(this.lineEndBAP,
-                    3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
-                    this.auxiliaryPosition, false));
-            baseList.add(this.makeZeroWidthPenalty(0));
-            baseList.add(new KnuthGlue(p2WidthOffset
-                    - (this.lineStartBAP + this.lineEndBAP), -6
+            elements.add(new KnuthGlue(lineEndBAP, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
+                    auxiliaryPosition, false));
+            elements.add(makeZeroWidthPenalty(0));
+            elements.add(new KnuthGlue(p2WidthOffset - (lineStartBAP + lineEndBAP), -6
                     * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, pos2, false));
-            baseList.add(this.makeAuxiliaryZeroWidthBox());
-            baseList.add(this.makeZeroWidthPenalty(KnuthElement.INFINITE));
-            baseList.add(new KnuthGlue(this.lineStartBAP + p3WidthOffset,
+            elements.add(makeAuxiliaryZeroWidthBox());
+            elements.add(makeZeroWidthPenalty(KnuthElement.INFINITE));
+            elements.add(new KnuthGlue(lineStartBAP + p3WidthOffset,
                     3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, pos3, false));
             break;
 
@@ -1065,246 +1103,203 @@
             // if the second element is chosen as a line break these elements
             // add a constant amount of stretch at the end of a line, otherwise
             // they don't add any stretch
-            if (skipZeroCheck || this.lineStartBAP != 0 || this.lineEndBAP != 0) {
-                baseList.add(new KnuthGlue(this.lineEndBAP,
-                        3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
-                        this.auxiliaryPosition, false));
-                baseList.add(this.makeZeroWidthPenalty(0));
-                baseList.add(new KnuthGlue(p2WidthOffset
-                                - (this.lineStartBAP + this.lineEndBAP), -3
-                                * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
-                                pos2, false));
-                baseList.add(this.makeAuxiliaryZeroWidthBox());
-                baseList.add(this.makeZeroWidthPenalty(KnuthElement.INFINITE));
-                baseList.add(new KnuthGlue(this.lineStartBAP + p3WidthOffset,
-                        0, 0, pos3, false));
+            if (skipZeroCheck || lineStartBAP != 0 || lineEndBAP != 0) {
+                elements.add(new KnuthGlue(lineEndBAP, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, auxiliaryPosition, false));
+                elements.add(makeZeroWidthPenalty(0));
+                elements.add(new KnuthGlue(p2WidthOffset - (lineStartBAP + lineEndBAP), -3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, pos2, false));
+                elements.add(makeAuxiliaryZeroWidthBox());
+                elements.add(makeZeroWidthPenalty(KnuthElement.INFINITE));
+                elements.add(new KnuthGlue(lineStartBAP + p3WidthOffset, 0, 0, pos3, false));
             } else {
-                baseList.add(new KnuthGlue(0,
-                        3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
-                        this.auxiliaryPosition, false));
-                baseList.add(this.makeZeroWidthPenalty(0));
-                baseList.add(new KnuthGlue(ai.areaIPD.opt, -3
-                                * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
-                                pos2, false));
+                elements.add(new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, auxiliaryPosition, false));
+                elements.add(makeZeroWidthPenalty(0));
+                elements.add(new KnuthGlue(areaInfo.areaIPD.getOpt(), -3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, pos2, false));
             }
             break;
 
         case EN_JUSTIFY:
             // justified text:
             // the stretch and shrink depends on the space width
-            if (skipZeroCheck || this.lineStartBAP != 0 || this.lineEndBAP != 0) {
-                baseList.add(new KnuthGlue(this.lineEndBAP, 0, 0,
-                        this.auxiliaryPosition, false));
-                baseList.add(this.makeZeroWidthPenalty(0));
-                baseList.add(new KnuthGlue(p2WidthOffset
-                        - (this.lineStartBAP + this.lineEndBAP), ai.areaIPD.max
-                        - ai.areaIPD.opt, ai.areaIPD.opt - ai.areaIPD.min,
-                        pos2, false));
-                baseList.add(this.makeAuxiliaryZeroWidthBox());
-                baseList.add(this.makeZeroWidthPenalty(KnuthElement.INFINITE));
-                baseList.add(new KnuthGlue(this.lineStartBAP + p3WidthOffset,
-                        0, 0, pos3, false));
-            } else {
-                baseList.add(new KnuthGlue(ai.areaIPD.opt, ai.areaIPD.max
-                        - ai.areaIPD.opt, ai.areaIPD.opt - ai.areaIPD.min,
-                        pos2, false));
-            }
+            elements.addAll(getElementsForJustifiedText(areaInfo, pos2, p2WidthOffset, pos3,
+                    p3WidthOffset, skipZeroCheck, areaInfo.areaIPD.getShrink()));
             break;
 
         default:
             // last line justified, the other lines unjustified:
             // use only the space stretch
-            if (skipZeroCheck || this.lineStartBAP != 0 || this.lineEndBAP != 0) {
-                baseList.add(new KnuthGlue(this.lineEndBAP, 0, 0,
-                        this.auxiliaryPosition, false));
-                baseList.add(this.makeZeroWidthPenalty(0));
-                baseList.add(new KnuthGlue(p2WidthOffset
-                        - (this.lineStartBAP + this.lineEndBAP), ai.areaIPD.max
-                        - ai.areaIPD.opt, 0, pos2, false));
-                baseList.add(this.makeAuxiliaryZeroWidthBox());
-                baseList.add(this.makeZeroWidthPenalty(KnuthElement.INFINITE));
-                baseList.add(new KnuthGlue(this.lineStartBAP + p3WidthOffset,
-                        0, 0, pos3, false));
-            } else {
-                baseList.add(new KnuthGlue(ai.areaIPD.opt, ai.areaIPD.max
-                        - ai.areaIPD.opt, 0, pos2, false));
-            }
+            elements.addAll(getElementsForJustifiedText(areaInfo, pos2, p2WidthOffset, pos3,
+                    p3WidthOffset, skipZeroCheck, 0));
         }
+        return elements;
     }
 
-    private void addElementsForAWordFragment(final List baseList,
-                                             final int alignment,
-                                             final AreaInfo ai,
-                                             final int leafValue,
-                                             final MinOptMax letterSpaceWidth) {
+    private List getElementsForJustifiedText(AreaInfo areaInfo, Position pos2, int p2WidthOffset,
+                                             Position pos3, int p3WidthOffset, boolean skipZeroCheck,
+                                             int shrinkability) {
+
+        int stretchability = areaInfo.areaIPD.getStretch();
+
+        List elements = new ArrayList();
+        if (skipZeroCheck || lineStartBAP != 0 || lineEndBAP != 0) {
+            elements.add(new KnuthGlue(lineEndBAP, 0, 0, auxiliaryPosition, false));
+            elements.add(makeZeroWidthPenalty(0));
+            elements.add(new KnuthGlue(p2WidthOffset - (lineStartBAP + lineEndBAP),
+                    stretchability, shrinkability, pos2, false));
+            elements.add(makeAuxiliaryZeroWidthBox());
+            elements.add(makeZeroWidthPenalty(KnuthElement.INFINITE));
+            elements.add(new KnuthGlue(lineStartBAP + p3WidthOffset, 0, 0, pos3, false));
+        } else {
+            elements.add(new KnuthGlue(areaInfo.areaIPD.getOpt(), stretchability, shrinkability,
+                    pos2, false));
+        }
+        return elements;
+    }
 
-        final LeafPosition mainPosition = new LeafPosition(this, leafValue);
+    private void addElementsForAWordFragment(List baseList, int alignment, AreaInfo areaInfo,
+                                             int leafValue) {
+        LeafPosition mainPosition = new LeafPosition(this, leafValue);
 
         // if the last character of the word fragment is '-' or '/',
         // the fragment could end a line; in this case, it loses one
         // of its letter spaces;
-        final boolean suppressibleLetterSpace = ai.breakOppAfter && !ai.isHyphenated;
+        boolean suppressibleLetterSpace = areaInfo.breakOppAfter && !areaInfo.isHyphenated;
 
-        if (letterSpaceWidth.min == letterSpaceWidth.max) {
+        if (letterSpaceIPD.isStiff()) {
             // constant letter spacing
-            baseList.add(new KnuthInlineBox(
-                        suppressibleLetterSpace
-                                ? ai.areaIPD.opt - letterSpaceWidth.opt
-                                : ai.areaIPD.opt,
-                        this.alignmentContext,
-                        this.notifyPos(mainPosition), false));
+            baseList.add(new KnuthInlineBox(suppressibleLetterSpace
+                    ? areaInfo.areaIPD.getOpt() - letterSpaceIPD.getOpt()
+                    : areaInfo.areaIPD.getOpt(),
+                    alignmentContext, notifyPos(mainPosition), false));
         } else {
             // adjustable letter spacing
-            final int unsuppressibleLetterSpaces
-                = suppressibleLetterSpace ? ai.letterSpaceCount - 1 : ai.letterSpaceCount;
-            baseList.add
-                (new KnuthInlineBox(ai.areaIPD.opt
-                        - ai.letterSpaceCount * letterSpaceWidth.opt,
-                        this.alignmentContext,
-                        this.notifyPos(mainPosition), false));
-            baseList.add(this.makeZeroWidthPenalty(KnuthElement.INFINITE));
-            baseList.add
-                (new KnuthGlue(unsuppressibleLetterSpaces * letterSpaceWidth.opt,
-                        unsuppressibleLetterSpaces * (letterSpaceWidth.max - letterSpaceWidth.opt),
-                        unsuppressibleLetterSpaces * (letterSpaceWidth.opt - letterSpaceWidth.min),
-                        this.auxiliaryPosition, true));
-            baseList.add(this.makeAuxiliaryZeroWidthBox());
+            int unsuppressibleLetterSpaces = suppressibleLetterSpace
+                    ? areaInfo.letterSpaceCount - 1
+                    : areaInfo.letterSpaceCount;
+            baseList.add(new KnuthInlineBox(areaInfo.areaIPD.getOpt()
+                    - areaInfo.letterSpaceCount * letterSpaceIPD.getOpt(),
+                            alignmentContext, notifyPos(mainPosition), false));
+            baseList.add(makeZeroWidthPenalty(KnuthElement.INFINITE));
+            baseList.add(new KnuthGlue(letterSpaceIPD.mult(unsuppressibleLetterSpaces),
+                    auxiliaryPosition, true));
+            baseList.add(makeAuxiliaryZeroWidthBox());
         }
 
         // extra-elements if the word fragment is the end of a syllable,
         // or it ends with a character that can be used as a line break
-        if (ai.isHyphenated) {
+        if (areaInfo.isHyphenated) {
             MinOptMax widthIfNoBreakOccurs = null;
-            if (ai.breakIndex < this.foText.length()) {
+            if (areaInfo.breakIndex < foText.length()) {
                 //Add in kerning in no-break condition
-                widthIfNoBreakOccurs = this.letterAdjustArray[ai.breakIndex];
+                widthIfNoBreakOccurs = letterAdjustArray[areaInfo.breakIndex];
             }
-            //if (ai.breakIndex)
+            //if (areaInfo.breakIndex)
 
             // the word fragment ends at the end of a syllable:
             // if a break occurs the content width increases,
             // otherwise nothing happens
-            this.addElementsForAHyphen(baseList, alignment, this.hyphIPD,
-                    widthIfNoBreakOccurs, ai.breakOppAfter && ai.isHyphenated);
+            addElementsForAHyphen(baseList, alignment, hyphIPD, widthIfNoBreakOccurs,
+                    areaInfo.breakOppAfter && areaInfo.isHyphenated);
         } else if (suppressibleLetterSpace) {
             // the word fragment ends with a character that acts as a hyphen
             // if a break occurs the width does not increase,
             // otherwise there is one more letter space
-            this.addElementsForAHyphen(baseList, alignment, 0, letterSpaceWidth, true);
+            addElementsForAHyphen(baseList, alignment, 0, letterSpaceIPD, true);
         }
     }
 
-    // static final int SOFT_HYPHEN_PENALTY = KnuthPenalty.FLAGGED_PENALTY / 10;
-    private static final int SOFT_HYPHEN_PENALTY = 1;
+    private void addElementsForAHyphen(List baseList, int alignment, int widthIfBreakOccurs,
+                                       MinOptMax widthIfNoBreakOccurs, boolean unflagged) {
 
-    private void addElementsForAHyphen(final List baseList,
-                                       final int alignment,
-                                       final int widthIfBreakOccurs,
-                                       MinOptMax widthIfNoBreakOccurs,
-                                       final boolean unflagged) {
         if (widthIfNoBreakOccurs == null) {
-            widthIfNoBreakOccurs = TextLayoutManager.ZERO_MINOPTMAX;
+            widthIfNoBreakOccurs = MinOptMax.ZERO;
         }
 
         switch (alignment) {
-        case EN_CENTER :
+        case EN_CENTER:
             // centered text:
-            baseList.add(this.makeZeroWidthPenalty(KnuthElement.INFINITE));
-            baseList.add(new KnuthGlue(this.lineEndBAP,
-                    3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
-                    this.auxiliaryPosition, true));
-            baseList.add(new KnuthPenalty(this.hyphIPD,
-                    unflagged ? TextLayoutManager.SOFT_HYPHEN_PENALTY
-                            : KnuthPenalty.FLAGGED_PENALTY, !unflagged,
-                    this.auxiliaryPosition, false));
-            baseList.add(new KnuthGlue(-(this.lineEndBAP + this.lineStartBAP),
+            baseList.add(makeZeroWidthPenalty(KnuthElement.INFINITE));
+            baseList.add(new KnuthGlue(lineEndBAP, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
+                    auxiliaryPosition, true));
+            baseList.add(new KnuthPenalty(hyphIPD, unflagged
+                    ? TextLayoutManager.SOFT_HYPHEN_PENALTY
+                            : KnuthPenalty.FLAGGED_PENALTY, !unflagged, auxiliaryPosition, false));
+            baseList.add(new KnuthGlue(-(lineEndBAP + lineStartBAP),
                     -6 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
-                    this.auxiliaryPosition, false));
-            baseList.add(this.makeAuxiliaryZeroWidthBox());
-            baseList.add(this.makeZeroWidthPenalty(KnuthElement.INFINITE));
-            baseList.add(new KnuthGlue(this.lineStartBAP,
-                    3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
-                    this.auxiliaryPosition, true));
+                    auxiliaryPosition, false));
+            baseList.add(makeAuxiliaryZeroWidthBox());
+            baseList.add(makeZeroWidthPenalty(KnuthElement.INFINITE));
+            baseList.add(new KnuthGlue(lineStartBAP, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH,
+                    0, auxiliaryPosition, true));
             break;
 
-        case EN_START  : // fall through
-        case EN_END    :
+        case EN_START: // fall through
+        case EN_END:
             // left- or right-aligned text:
-            if (this.lineStartBAP != 0 || this.lineEndBAP != 0) {
-                baseList.add(this.makeZeroWidthPenalty(KnuthElement.INFINITE));
-                baseList.add(new KnuthGlue(this.lineEndBAP,
+            if (lineStartBAP != 0 || lineEndBAP != 0) {
+                baseList.add(makeZeroWidthPenalty(KnuthElement.INFINITE));
+                baseList.add(new KnuthGlue(lineEndBAP,
                         3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
-                        this.auxiliaryPosition, false));
+                        auxiliaryPosition, false));
                 baseList.add(new KnuthPenalty(widthIfBreakOccurs,
                         unflagged ? TextLayoutManager.SOFT_HYPHEN_PENALTY
                                 : KnuthPenalty.FLAGGED_PENALTY, !unflagged,
-                        this.auxiliaryPosition, false));
-                baseList.add(new KnuthGlue(widthIfNoBreakOccurs.opt
-                        - (this.lineStartBAP + this.lineEndBAP), -3
-                        * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
-                        this.auxiliaryPosition, false));
-                baseList.add(this.makeAuxiliaryZeroWidthBox());
-                baseList.add(this.makeZeroWidthPenalty(KnuthElement.INFINITE));
-                baseList.add(new KnuthGlue(this.lineStartBAP, 0, 0,
-                                   this.auxiliaryPosition, false));
+                                auxiliaryPosition, false));
+                baseList.add(new KnuthGlue(widthIfNoBreakOccurs.getOpt()
+                        - (lineStartBAP + lineEndBAP),
+                        -3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
+                        auxiliaryPosition, false));
+                baseList.add(makeAuxiliaryZeroWidthBox());
+                baseList.add(makeZeroWidthPenalty(KnuthElement.INFINITE));
+                baseList.add(new KnuthGlue(lineStartBAP, 0, 0, auxiliaryPosition, false));
             } else {
-                baseList.add(this.makeZeroWidthPenalty(KnuthElement.INFINITE));
+                baseList.add(makeZeroWidthPenalty(KnuthElement.INFINITE));
                 baseList.add(new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
-                            this.auxiliaryPosition, false));
+                        auxiliaryPosition, false));
                 baseList.add(new KnuthPenalty(widthIfBreakOccurs,
                         unflagged ? TextLayoutManager.SOFT_HYPHEN_PENALTY
                                 : KnuthPenalty.FLAGGED_PENALTY, !unflagged,
-                        this.auxiliaryPosition, false));
-                baseList.add(new KnuthGlue(widthIfNoBreakOccurs.opt,
-                            -3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
-                            this.auxiliaryPosition, false));
+                                auxiliaryPosition, false));
+                baseList.add(new KnuthGlue(widthIfNoBreakOccurs.getOpt(),
+                        -3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
+                        auxiliaryPosition, false));
             }
             break;
 
         default:
             // justified text, or last line justified:
             // just a flagged penalty
-            if (this.lineStartBAP != 0 || this.lineEndBAP != 0) {
-                baseList.add(this.makeZeroWidthPenalty(KnuthElement.INFINITE));
-                baseList.add(new KnuthGlue(this.lineEndBAP, 0, 0,
-                                   this.auxiliaryPosition, false));
+            if (lineStartBAP != 0 || lineEndBAP != 0) {
+                baseList.add(makeZeroWidthPenalty(KnuthElement.INFINITE));
+                baseList.add(new KnuthGlue(lineEndBAP, 0, 0, auxiliaryPosition, false));
                 baseList.add(new KnuthPenalty(widthIfBreakOccurs,
                         unflagged ? TextLayoutManager.SOFT_HYPHEN_PENALTY
                                 : KnuthPenalty.FLAGGED_PENALTY, !unflagged,
-                        this.auxiliaryPosition, false));
+                                auxiliaryPosition, false));
                 // extra elements representing a letter space that is suppressed
                 // if a break occurs
-                if (widthIfNoBreakOccurs.min != 0
-                    || widthIfNoBreakOccurs.max != 0) {
-                    baseList
-                            .add(new KnuthGlue(widthIfNoBreakOccurs.opt
-                                    - (this.lineStartBAP + this.lineEndBAP),
-                                    widthIfNoBreakOccurs.max
-                                            - widthIfNoBreakOccurs.opt,
-                                    widthIfNoBreakOccurs.opt
-                                            - widthIfNoBreakOccurs.min,
-                                    this.auxiliaryPosition, false));
+                if (widthIfNoBreakOccurs.isNonZero()) {
+                    baseList.add(new KnuthGlue(widthIfNoBreakOccurs.getOpt()
+                            - (lineStartBAP + lineEndBAP),
+                            widthIfNoBreakOccurs.getStretch(),
+                            widthIfNoBreakOccurs.getShrink(),
+                            auxiliaryPosition, false));
                 } else {
-                    baseList.add(new KnuthGlue(-(this.lineStartBAP + this.lineEndBAP), 0, 0,
-                                       this.auxiliaryPosition, false));
+                    baseList.add(new KnuthGlue(-(lineStartBAP + lineEndBAP), 0, 0,
+                            auxiliaryPosition, false));
                 }
-                baseList.add(this.makeAuxiliaryZeroWidthBox());
-                baseList.add(this.makeZeroWidthPenalty(KnuthElement.INFINITE));
-                baseList.add(new KnuthGlue(this.lineStartBAP, 0, 0,
-                                   this.auxiliaryPosition, false));
+                baseList.add(makeAuxiliaryZeroWidthBox());
+                baseList.add(makeZeroWidthPenalty(KnuthElement.INFINITE));
+                baseList.add(new KnuthGlue(lineStartBAP, 0, 0,
+                        auxiliaryPosition, false));
             } else {
                 baseList.add(new KnuthPenalty(widthIfBreakOccurs,
                         unflagged ? TextLayoutManager.SOFT_HYPHEN_PENALTY
                                 : KnuthPenalty.FLAGGED_PENALTY, !unflagged,
-                        this.auxiliaryPosition, false));
+                                auxiliaryPosition, false));
                 // extra elements representing a letter space that is suppressed
                 // if a break occurs
-                if (widthIfNoBreakOccurs.min != 0
-                    || widthIfNoBreakOccurs.max != 0) {
-                    baseList.add(new KnuthGlue(widthIfNoBreakOccurs.opt,
-                                widthIfNoBreakOccurs.max - widthIfNoBreakOccurs.opt,
-                                widthIfNoBreakOccurs.opt - widthIfNoBreakOccurs.min,
-                                this.auxiliaryPosition, false));
+                if (widthIfNoBreakOccurs.isNonZero()) {
+                    baseList.add(new KnuthGlue(widthIfNoBreakOccurs, auxiliaryPosition, false));
                 }
             }
         }

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/WrapperLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/WrapperLayoutManager.java?rev=893238&r1=893237&r2=893238&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/WrapperLayoutManager.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/inline/WrapperLayoutManager.java Tue Dec 22 17:20:51 2009
@@ -28,8 +28,6 @@
 import org.apache.fop.layoutmgr.PositionIterator;
 import org.apache.fop.layoutmgr.TraitSetter;
 
-import java.util.LinkedList;
-
 /**
  * This is the layout manager for the fo:wrapper formatting object.
  */
@@ -67,14 +65,14 @@
     public void addAreas(PositionIterator posIter, LayoutContext context) {
         if (fobj.hasId()) {
             addId();
-            if (parentLM instanceof BlockStackingLayoutManager
-                    && !(parentLM instanceof BlockLayoutManager)) {
+            if (parentLayoutManager instanceof BlockStackingLayoutManager
+                    && !(parentLayoutManager instanceof BlockLayoutManager)) {
                 Block helperBlock = new Block();
                 TraitSetter.setProducerID(helperBlock, fobj.getId());
-                parentLM.addChildArea(helperBlock);
+                parentLayoutManager.addChildArea(helperBlock);
             } else {
                 InlineArea area = getEffectiveArea();
-                parentLM.addChildArea(area);
+                parentLayoutManager.addChildArea(area);
             }
         }
         while (posIter.hasNext()) {

Modified: xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java
URL: http://svn.apache.org/viewvc/xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java?rev=893238&r1=893237&r2=893238&view=diff
==============================================================================
--- xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java (original)
+++ xmlgraphics/fop/trunk/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java Tue Dec 22 17:20:51 2009
@@ -154,7 +154,7 @@
         // if this will create the first block area in a page
         // and display-align is after or center, add space before
         if (layoutContext.getSpaceBefore() > 0) {
-            addBlockSpacing(0.0, new MinOptMax(layoutContext.getSpaceBefore()));
+            addBlockSpacing(0.0, MinOptMax.getInstance(layoutContext.getSpaceBefore()));
         }
 
         addId();
@@ -241,7 +241,7 @@
 
             // Set up dimensions
             // Must get dimensions from parent area
-            /*Area parentArea =*/ parentLM.getParentArea(curBlockArea);
+            /*Area parentArea =*/ parentLayoutManager.getParentArea(curBlockArea);
 
             // set traits
             TraitSetter.setProducerID(curBlockArea, getListBlockFO().getId());



---------------------------------------------------------------------
To unsubscribe, e-mail: fop-commits-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: fop-commits-help@xmlgraphics.apache.org