]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Patch 31206 by Luca Furini:
authorSimon Pepping <spepping@apache.org>
Sat, 13 Nov 2004 20:37:17 +0000 (20:37 +0000)
committerSimon Pepping <spepping@apache.org>
Sat, 13 Nov 2004 20:37:17 +0000 (20:37 +0000)
- A new interface InlineLevelLayoutManager, implemented by LeafNodeLM,
  InlineLM, TextLM, BasicLinkLM and ContentLM

- Leaders with pattern "use-content" and "dots" now works

- Better handling of preserved linefeeds

- Correct calculation of the lineheight, according to the value of the
  property vertical-align

For a few points which may need further work, see the bugzilla page.

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@198142 13f79535-47bb-0310-9956-ffa450edef68

19 files changed:
src/java/org/apache/fop/area/inline/FilledArea.java
src/java/org/apache/fop/fo/flow/Character.java
src/java/org/apache/fop/fo/flow/Inline.java
src/java/org/apache/fop/fo/flow/Leader.java
src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java
src/java/org/apache/fop/layoutmgr/BasicLinkLayoutManager.java
src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
src/java/org/apache/fop/layoutmgr/CharacterLayoutManager.java
src/java/org/apache/fop/layoutmgr/ContentLayoutManager.java
src/java/org/apache/fop/layoutmgr/InlineLayoutManager.java
src/java/org/apache/fop/layoutmgr/InlineLevelLayoutManager.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/InlineStackingLayoutManager.java
src/java/org/apache/fop/layoutmgr/LayoutContext.java
src/java/org/apache/fop/layoutmgr/LayoutManager.java
src/java/org/apache/fop/layoutmgr/LeaderLayoutManager.java
src/java/org/apache/fop/layoutmgr/LeafNodeLayoutManager.java
src/java/org/apache/fop/layoutmgr/LineLayoutManager.java
src/java/org/apache/fop/layoutmgr/RetrieveMarkerLayoutManager.java
src/java/org/apache/fop/layoutmgr/TextLayoutManager.java

index afbcbe73ff76500d01a39d4d1755848760a19cd0..84e905536addb9c5a8ea77dadea72a702a1fb148 100644 (file)
@@ -19,6 +19,7 @@
 package org.apache.fop.area.inline;
 
 import java.util.List;
+import java.util.ListIterator;
 import java.util.ArrayList;
 
 /**
@@ -39,6 +40,29 @@ public class FilledArea extends InlineParent {
     public FilledArea() {
     }
 
+    /**
+     * Set the offset of the descendant TextAreas,
+     * instead of the offset of the FilledArea itself.
+     *
+     * @param v the offset
+     */
+    public void setOffset(int v) {
+        setChildOffset(inlines.listIterator(), v);
+    }
+
+    private void setChildOffset(ListIterator childrenIterator, int v) {
+        while (childrenIterator.hasNext()) {
+            InlineArea child = (InlineArea) childrenIterator.next();
+            if (child instanceof InlineParent) {
+                setChildOffset(((InlineParent) child).getChildAreas().listIterator(), v);
+            } else if (child instanceof org.apache.fop.area.inline.Viewport) {
+                // nothing
+            } else {
+                child.setOffset(v);
+            }
+        }
+    }
+
     /**
      * Set the unit width for the areas to fill the full width.
      *
index a43ebbd4f9f648e29622bc781290044528eedc62..445f9d339ae26ab95ec3ee72d8cba5c5206a2af8 100644 (file)
@@ -84,6 +84,7 @@ public class Character extends FObj {
     private int textDecoration;
     // private ToBeImplementedProperty textShadow;
     private int textTransform;
+    private int verticalAlign;
     private int visibility;
     private Property wordSpacing;
     // End of property values
@@ -132,6 +133,7 @@ public class Character extends FObj {
         textDecoration = pList.get(PR_TEXT_DECORATION).getEnum();
         // textShadow = pList.get(PR_TEXT_SHADOW);
         textTransform = pList.get(PR_TEXT_TRANSFORM).getEnum();
+        verticalAlign = pList.get(PR_VERTICAL_ALIGN).getEnum();
         visibility = pList.get(PR_VISIBILITY).getEnum();
         wordSpacing = pList.get(PR_WORD_SPACING);
     }
@@ -224,6 +226,13 @@ public class Character extends FObj {
         return wordSpacing; 
     }
 
+    /**
+     * Return the "vertical-align" property.
+     */
+    public int getVerticalAlign() {
+        return verticalAlign; 
+    }
+
     /**
      * @see org.apache.fop.fo.FONode#addLayoutManager(List)
      */
index 602dbd99ebadbe097c720f6af41756dfc02fa038..9084d4ae908385b301a2171cb3933e7ca1294037 100644 (file)
@@ -66,6 +66,7 @@ public class Inline extends FObjMixed {
     private KeepProperty keepWithPrevious;
     private Length lineHeight;
     private int textDecoration;
+    private int verticalAlign;
     private int visibility;
     private Length width;
     private int wrapOption;
@@ -106,6 +107,7 @@ public class Inline extends FObjMixed {
         keepWithPrevious = pList.get(PR_KEEP_WITH_PREVIOUS).getKeep();
         lineHeight = pList.get(PR_LINE_HEIGHT).getLength();
         textDecoration = pList.get(PR_TEXT_DECORATION).getEnum();
+        verticalAlign = pList.get(PR_VERTICAL_ALIGN).getEnum();
         visibility = pList.get(PR_VISIBILITY).getEnum();
         width = pList.get(PR_WIDTH).getLength();
         wrapOption = pList.get(PR_WRAP_OPTION).getEnum();
@@ -214,6 +216,13 @@ public class Inline extends FObjMixed {
         return textDecoration; 
     }
 
+    /**
+     * Return the "vertical-align" property.
+     */
+    public int getVerticalAlign() {
+        return verticalAlign; 
+    }
+
     /**
      * @see org.apache.fop.fo.FObjMixed#charIterator
      */
index 5fb3b4fb5a516a220afafe828dfe4e0cff556269..51d8caafaeeac0aaf2d0e86134f46d5c4b95a535 100644 (file)
@@ -54,6 +54,7 @@ public class Leader extends FObjMixed {
     private CommonRelativePosition commonRelativePosition;
     private Length alignmentAdjust;
     private int alignmentBaseline;
+    private int verticalAlign;
     private Length baselineShift;
     private ColorType color;
     private int dominantBaseline;
@@ -94,6 +95,7 @@ public class Leader extends FObjMixed {
         commonRelativePosition = pList.getRelativePositionProps();
         alignmentAdjust = pList.get(PR_ALIGNMENT_ADJUST).getLength();
         alignmentBaseline = pList.get(PR_ALIGNMENT_BASELINE).getEnum();
+        verticalAlign = pList.get(PR_VERTICAL_ALIGN).getEnum();
         baselineShift = pList.get(PR_BASELINE_SHIFT).getLength();
         color = pList.get(PR_COLOR).getColorType();
         dominantBaseline = pList.get(PR_DOMINANT_BASELINE).getEnum();
@@ -136,7 +138,21 @@ public class Leader extends FObjMixed {
     protected void startOfNode() throws FOPException {
         checkId(id);
     }
+
+    /**
+     * Return the Common Margin Properties-Inline.
+     */
+    public CommonMarginInline getCommonMarginInline() {
+        return commonMarginInline;
+    }
+
+    /**
+     * Return the Common Border, Padding, and Background Properties.
+     */
+    public CommonBorderPaddingBackground getCommonBorderPaddingBackground() {
+        return commonBorderPaddingBackground;
+    } 
+
     /**
      * Return the Common Font Properties.
      */
@@ -193,6 +209,13 @@ public class Leader extends FObjMixed {
         return leaderPatternWidth;
     }
 
+    /**
+     * Return the "vertical-align" property.
+     */
+    public int getVerticalAlign() {
+        return verticalAlign; 
+    }
+
     /**
      * @see org.apache.fop.fo.FONode#addLayoutManager(List)
      */
index 125b4c52c05b664d481c88329ef517794575120f..f5aa7d22acdad45a75b7a83d683f08ae1ff87f0e 100644 (file)
@@ -275,43 +275,6 @@ public abstract class AbstractLayoutManager implements LayoutManager, Constants
      * interface which are declared abstract in AbstractLayoutManager.
      * ---------------------------------------------------------*/
 
-    public LinkedList getNextKnuthElements(LayoutContext context,
-                                           int alignment) {
-        log.debug("null implementation of getNextKnuthElements() called!");
-        setFinished(true);
-        return null;
-    }
-
-    public KnuthElement addALetterSpaceTo(KnuthElement element) {
-        log.debug("null implementation of addALetterSpaceTo() called!");
-        return element;
-    }
-
-    public void getWordChars(StringBuffer sbChars, Position pos) {
-        log.debug("null implementation of getWordChars() called!");
-    }
-
-    public void hyphenate(Position pos, HyphContext hc) {
-        log.debug("null implementation of hyphenate called!");
-    }
-
-    public boolean applyChanges(List oldList) {
-        log.debug("null implementation of applyChanges() called!");
-        return false;
-    }
-
-    public LinkedList getChangedKnuthElements(List oldList,
-                                              int flaggedPenalty,
-                                              int alignment) {
-        log.debug("null implementation of getChangeKnuthElement() called!");
-        return null;
-    }
-
-    public int getWordSpaceIPD() {
-        log.debug("null implementation of getWordSpaceIPD() called!");
-        return 0;
-    }
-
     public Area getParentArea(Area childArea) {
         return null;
     }
index d28a7a3434c60ee6419965304cea6ff13c29903a..528ce4e6faad27e183b4c9b6fed055e1c160734c 100644 (file)
@@ -27,7 +27,7 @@ import org.apache.fop.area.PageViewport;
 /**
  * LayoutManager for the fo:basic-link formatting object
  */
-public class BasicLinkLayoutManager extends InlineStackingLayoutManager {
+public class BasicLinkLayoutManager extends InlineLayoutManager {
     private BasicLink fobj;
     
     /**
index edf25f17c564f7f7e136e2cef0243dc0f55aac17..51ba1f1ae49c3b67aeddbf27d298d169c21bb567 100644 (file)
@@ -57,6 +57,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
     private int lead = 12000;
     private int lineHeight = 14000;
     private int follow = 2000;
+    private int middleShift = 0;
 
     private int iStartPos = 0;
 
@@ -71,6 +72,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
         
         lead = fs.getAscender();
         follow = -fs.getDescender();
+        middleShift = -fs.getXHeight() / 2;
         lineHeight = fobj.getLineHeight().getOptimum().getLength().getValue();
     }
 
@@ -141,7 +143,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
      */
     private LineLayoutManager createLineManager(LayoutManager firstlm) {
         LineLayoutManager llm;
-        llm = new LineLayoutManager(fobj, lineHeight, lead, follow);
+        llm = new LineLayoutManager(fobj, lineHeight, lead, follow, middleShift);
         List inlines = new ArrayList();
         inlines.add(firstlm);
         while (proxyLMiter.hasNext()) {
index b7b579a8d1021da277463b2bc75899d19dafa957..431d244d5136a2788f7a7ae81a300ef6ea93a11c 100644 (file)
@@ -48,6 +48,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
         fobj = node;
         InlineArea inline = getCharacterInlineArea(node);
         setCurrentArea(inline);
+        setAlignment(fobj.getVerticalAlign());
         fs = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo());
 
         SpaceVal ls = SpaceVal.makeLetterSpacing(fobj.getLetterSpacing());
@@ -71,15 +72,15 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
      */
     protected void offsetArea(LayoutContext context) {
         int bpd = curArea.getBPD();
-        switch (alignment) {
+        switch (verticalAlignment) {
             case VerticalAlign.MIDDLE:
-                curArea.setOffset(context.getBaseline() - bpd / 2 /* - fontLead/2 */);
+                curArea.setOffset(context.getMiddleBaseline() + fs.getXHeight() / 2);
             break;
             case VerticalAlign.TOP:
-                //curArea.setOffset(0);
+                curArea.setOffset(fs.getAscender());
             break;
             case VerticalAlign.BOTTOM:
-                curArea.setOffset(context.getLineHeight() - bpd);
+                curArea.setOffset(context.getLineHeight() - bpd + fs.getAscender());
             break;
             case VerticalAlign.BASELINE:
             default:
@@ -116,16 +117,15 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
         int lead = 0;
         int total = 0;
         int middle = 0;
-        switch (alignment) {
+        switch (verticalAlignment) {
             case VerticalAlign.MIDDLE  : middle = bpd / 2 ;
-                                         lead = bpd / 2 ;
-                                         break;
-            case VerticalAlign.TOP     : total = bpd;
                                          break;
+            case VerticalAlign.TOP     : // fall through
             case VerticalAlign.BOTTOM  : total = bpd;
                                          break;
-            case VerticalAlign.BASELINE:
-            default:                     lead = bpd;
+            case VerticalAlign.BASELINE: // fall through
+            default                    : lead = fs.getAscender();
+                                         total = bpd;
                                          break;
         }
 
index 08d54537f56d0f292bf909b6c11ee90b8e8a5112..d8eb3fbb08164f6b8faf65c381c3f191471a84cf 100644 (file)
@@ -22,6 +22,7 @@ import org.apache.fop.fo.FObj;
 import org.apache.fop.apps.FOUserAgent;
 import org.apache.fop.fo.flow.Marker;
 import org.apache.fop.area.Area;
+import org.apache.fop.area.inline.InlineArea;
 import org.apache.fop.area.Resolvable;
 import org.apache.fop.area.PageViewport;
 
@@ -40,12 +41,12 @@ import org.apache.commons.logging.LogFactory;
  * For use with objects that contain inline areas such as
  * leader use-content and title.
  */
-public class ContentLayoutManager implements LayoutManager {
+public class ContentLayoutManager implements InlineLevelLayoutManager {
     private FOUserAgent userAgent;
     private Area holder;
     private int stackSize;
     private LayoutManager parentLM;
-    private List childLMs = new ArrayList(1);
+    private InlineLevelLayoutManager childLM = null;
 
     /**
      * logging instance
@@ -137,6 +138,19 @@ public class ContentLayoutManager implements LayoutManager {
         stackSize = stack.opt;
     }
 
+    public void addAreas(PositionIterator posIter, LayoutContext context) {
+        // add the content areas
+        // the area width has already been adjusted, and it must remain unchanged
+        // so save its value before calling addAreas, and set it again afterwards
+        int savedIPD = ((InlineArea)holder).getIPD();
+        // set to zero the ipd adjustment ratio, to avoid spaces in the pattern
+        // to be modified
+        LayoutContext childContext = new LayoutContext(context);
+        childContext.setIPDAdjust(0.0);
+        childLM.addAreas(posIter, childContext);
+        ((InlineArea)holder).setIPD(savedIPD);
+    }
+
     public int getStackingSize() {
         return stackSize;
     }
@@ -201,9 +215,6 @@ public class ContentLayoutManager implements LayoutManager {
         //to be done
     }
 
-    /** @see org.apache.fop.layoutmgr.LayoutManager */
-    public void addAreas(PositionIterator posIter, LayoutContext context) { }
-
     /** @see org.apache.fop.layoutmgr.LayoutManager */
     public void initialize() {
         //to be done
@@ -259,6 +270,8 @@ public class ContentLayoutManager implements LayoutManager {
      * @see org.apache.fop.layoutmgr.LayoutManager#getChildLMs
      */
     public List getChildLMs() {
+        List childLMs = new ArrayList(1);
+        childLMs.add(childLM);
         return childLMs;
     }
 
@@ -271,7 +284,7 @@ public class ContentLayoutManager implements LayoutManager {
         }
         lm.setParent(this);
         lm.initialize();
-        childLMs.add(lm);
+        childLM = (InlineLevelLayoutManager)lm;
         log.trace(this.getClass().getName()
                   + ": Adding child LM " + lm.getClass().getName());
     }
@@ -292,8 +305,26 @@ public class ContentLayoutManager implements LayoutManager {
 
     public LinkedList getNextKnuthElements(LayoutContext context,
                                            int alignment) {
+        LinkedList contentList = new LinkedList();
+        LinkedList returnedList;
+
+        while (!childLM.isFinished()) {
+            // get KnuthElements from childLM
+            returnedList = childLM.getNextKnuthElements(context, alignment);
+
+            if (returnedList != null) {
+                // move elements to contentList, and accumulate their size
+               KnuthElement contentElement;
+               while (returnedList.size() > 0) {
+                    contentElement = (KnuthElement)returnedList.removeFirst();
+                    stackSize += contentElement.getW();
+                    contentList.add(contentElement);
+                }
+            }
+        }
+
         setFinished(true);
-        return null;
+        return contentList;
     }
 
     public KnuthElement addALetterSpaceTo(KnuthElement element) {
@@ -315,9 +346,5 @@ public class ContentLayoutManager implements LayoutManager {
                                               int alignment) {
         return null;
     }
-
-    public int getWordSpaceIPD() {
-        return 0;
-    }
 }
 
index 0d32cdb8ba8135a07a1b4fc8a6f5b82f044cfd37..a3b8d403d2fd410669afd577d460c86e9b8cb97d 100755 (executable)
 
 package org.apache.fop.layoutmgr;
 
+import java.util.List;
+import java.util.ListIterator;
+import java.util.LinkedList;
 
+import org.apache.fop.fo.FObj;
 import org.apache.fop.fo.flow.Inline;
+import org.apache.fop.fo.flow.Leader;
 import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
 import org.apache.fop.fo.properties.CommonMarginInline;
 import org.apache.fop.fo.properties.SpaceProperty;
@@ -30,8 +35,9 @@ import org.apache.fop.traits.SpaceVal;
  * LayoutManager for objects which stack children in the inline direction,
  * such as Inline or Line
  */
-public class InlineLayoutManager extends InlineStackingLayoutManager {
-    private Inline fobj;
+public class InlineLayoutManager extends InlineStackingLayoutManager 
+                                         implements InlineLevelLayoutManager {
+    private FObj fobj;
 
     private CommonMarginInline inlineProps = null;
     private CommonBorderPaddingBackground borderProps = null;
@@ -47,13 +53,31 @@ public class InlineLayoutManager extends InlineStackingLayoutManager {
         super(node);
         fobj = node;
     }
+
+    /**
+     * Create an inline layout manager.
+     * This is used for fo's that create areas that
+     * contain inline areas.
+     *
+     * @param node the formatting object that creates the area
+     */
+    public InlineLayoutManager(Leader node) {
+        super(node);
+        fobj = node;
+    }
     
     /**
      * @see org.apache.fop.layoutmgr.AbstractLayoutManager#initProperties()
      */
     protected void initProperties() {
-        inlineProps = fobj.getCommonMarginInline();
-        borderProps = fobj.getCommonBorderPaddingBackground();
+        // fobj can be either an Inline or a Leader
+        if (fobj instanceof Inline) {
+            inlineProps = ((Inline) fobj).getCommonMarginInline();
+            borderProps = ((Inline) fobj).getCommonBorderPaddingBackground();
+        } else {
+            inlineProps = ((Leader) fobj).getCommonMarginInline();
+            borderProps = ((Leader) fobj).getCommonBorderPaddingBackground();
+        }
         int iPad = borderProps.getPadding(CommonBorderPaddingBackground.BEFORE, false);
         iPad += borderProps.getBorderWidth(CommonBorderPaddingBackground.BEFORE,
                                              false);
@@ -94,7 +118,6 @@ public class InlineLayoutManager extends InlineStackingLayoutManager {
         return inlineProps.spaceEnd;
     }
     
-
     /**
      * Return value indicating whether the next area to be generated could
      * start a new line. This should only be called in the "START" condition
@@ -121,5 +144,204 @@ public class InlineLayoutManager extends InlineStackingLayoutManager {
         }
     }
 
+    public LinkedList getNextKnuthElements(LayoutContext lc, int alignment) {
+        InlineLevelLayoutManager curLM;
+
+        // the list returned by child LM
+        LinkedList returnedList;
+        KnuthElement returnedElement;
+
+        // the list which will be returned to the parent LM
+        LinkedList returnList = new LinkedList();
+
+        SpaceSpecifier leadingSpace = lc.getLeadingSpace();
+
+        if (lc.startsNewArea()) {
+            // First call to this LM in new parent "area", but this may
+            // not be the first area created by this inline
+            childLC = new LayoutContext(lc);
+            if (getSpaceStart() != null) {
+                lc.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart()));
+            }
+
+            // Check for "fence"
+            if (hasLeadingFence(!lc.isFirstArea())) {
+                // Reset leading space sequence for child areas
+                leadingSpace = new SpaceSpecifier(false);
+            }
+            // Reset state variables
+            clearPrevIPD(); // Clear stored prev content dimensions
+        }
+
+        while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) {
+            // get KnuthElements from curLM
+            returnedList = curLM.getNextKnuthElements(lc, alignment);
+            if (returnedList != null) {
+                // "wrap" the Position stored in each element of returnedList
+                ListIterator listIter = returnedList.listIterator();
+                while (listIter.hasNext()) {
+                    returnedElement = (KnuthElement) listIter.next();
+                    returnedElement.setPosition
+                        (new NonLeafPosition(this,
+                                             returnedElement.getPosition()));
+                    returnList.add(returnedElement);
+                }
+                return returnList;
+            } else {
+                // curLM returned null because it finished;
+                // just iterate once more to see if there is another child
+            }
+        }
+        setFinished(true);
+        return null;
+    }
+
+    public KnuthElement addALetterSpaceTo(KnuthElement element) {
+        NonLeafPosition savedPos = (NonLeafPosition) element.getPosition();
+        element.setPosition(savedPos.getPosition());
+
+        KnuthElement newElement
+            = ((InlineLevelLayoutManager)
+               element.getLayoutManager()).addALetterSpaceTo(element);
+        newElement.setPosition
+            (new NonLeafPosition(this, newElement.getPosition()));
+        element.setPosition(savedPos);
+        return newElement;
+    }
+
+    public void getWordChars(StringBuffer sbChars, Position pos) {
+        Position newPos = ((NonLeafPosition) pos).getPosition();
+        ((InlineLevelLayoutManager)
+         newPos.getLM()).getWordChars(sbChars, newPos);
+    }
+
+    public void hyphenate(Position pos, HyphContext hc) {
+        Position newPos = ((NonLeafPosition) pos).getPosition();
+        ((InlineLevelLayoutManager)
+         newPos.getLM()).hyphenate(newPos, hc);
+    }
+
+    public boolean applyChanges(List oldList) {
+        // "unwrap" the Positions stored in the elements
+        ListIterator oldListIterator = oldList.listIterator();
+        KnuthElement oldElement;
+        while (oldListIterator.hasNext()) {
+            oldElement = (KnuthElement) oldListIterator.next();
+            oldElement.setPosition
+                (((NonLeafPosition) oldElement.getPosition()).getPosition());
+        }
+        // reset the iterator
+        oldListIterator = oldList.listIterator();
+
+        InlineLevelLayoutManager prevLM = null;
+        InlineLevelLayoutManager currLM;
+        int fromIndex = 0;
+
+        boolean bSomethingChanged = false;
+        while(oldListIterator.hasNext()) {
+            oldElement = (KnuthElement) oldListIterator.next();
+            currLM = (InlineLevelLayoutManager) oldElement.getLayoutManager();
+            // initialize prevLM
+            if (prevLM == null) {
+                prevLM = currLM;
+            }
+
+            if (currLM != prevLM || !oldListIterator.hasNext()) {
+                if (oldListIterator.hasNext()) {
+                    bSomethingChanged
+                        = prevLM.applyChanges(oldList.subList(fromIndex, oldListIterator.previousIndex()))
+                        || bSomethingChanged;
+                    prevLM = currLM;
+                    fromIndex = oldListIterator.previousIndex();
+                } else if (currLM == prevLM) {
+                    bSomethingChanged
+                        = prevLM.applyChanges(oldList.subList(fromIndex, oldList.size()))
+                        || bSomethingChanged;
+                } else {
+                    bSomethingChanged
+                        = prevLM.applyChanges(oldList.subList(fromIndex, oldListIterator.previousIndex()))
+                        || bSomethingChanged;
+                    bSomethingChanged
+                        = currLM.applyChanges(oldList.subList(oldListIterator.previousIndex(), oldList.size()))
+                        || bSomethingChanged;
+                }
+            }
+        }
+
+        // "wrap" again the Positions stored in the elements
+        oldListIterator = oldList.listIterator();
+        while (oldListIterator.hasNext()) {
+            oldElement = (KnuthElement) oldListIterator.next();
+            oldElement.setPosition
+                (new NonLeafPosition(this, oldElement.getPosition()));
+        }
+        return bSomethingChanged;
+    }
+
+    public LinkedList getChangedKnuthElements(List oldList, int flaggedPenalty, int alignment) {
+        // "unwrap" the Positions stored in the elements
+        ListIterator oldListIterator = oldList.listIterator();
+        KnuthElement oldElement;
+        while (oldListIterator.hasNext()) {
+            oldElement = (KnuthElement) oldListIterator.next();
+            oldElement.setPosition
+                (((NonLeafPosition) oldElement.getPosition()).getPosition());
+        }
+        // reset the iterator
+        oldListIterator = oldList.listIterator();
+
+        KnuthElement returnedElement;
+        LinkedList returnedList = new LinkedList();
+        LinkedList returnList = new LinkedList();
+        InlineLevelLayoutManager prevLM = null;
+        InlineLevelLayoutManager currLM;
+        int fromIndex = 0;
+
+        while(oldListIterator.hasNext()) {
+            oldElement = (KnuthElement) oldListIterator.next();
+            currLM = (InlineLevelLayoutManager) oldElement.getLayoutManager();
+            if (prevLM == null) {
+                prevLM = currLM;
+            }
+
+            if (currLM != prevLM || !oldListIterator.hasNext()) {
+                if (oldListIterator.hasNext()) {
+                    returnedList.addAll
+                        (prevLM.getChangedKnuthElements
+                         (oldList.subList(fromIndex,
+                                          oldListIterator.previousIndex()),
+                          flaggedPenalty, alignment));
+                    prevLM = currLM;
+                    fromIndex = oldListIterator.previousIndex();
+                } else if (currLM == prevLM) {
+                    returnedList.addAll
+                        (prevLM.getChangedKnuthElements
+                         (oldList.subList(fromIndex, oldList.size()),
+                          flaggedPenalty, alignment));
+                } else {
+                    returnedList.addAll
+                        (prevLM.getChangedKnuthElements
+                         (oldList.subList(fromIndex,
+                                          oldListIterator.previousIndex()),
+                          flaggedPenalty, alignment));
+                    returnedList.addAll
+                        (currLM.getChangedKnuthElements
+                         (oldList.subList(oldListIterator.previousIndex(),
+                                          oldList.size()),
+                          flaggedPenalty, alignment));
+                }
+            }
+        }
+
+        // "wrap" the Position stored in each element of returnedList
+        ListIterator listIter = returnedList.listIterator();
+        while (listIter.hasNext()) {
+            returnedElement = (KnuthElement) listIter.next();
+            returnedElement.setPosition
+                (new NonLeafPosition(this, returnedElement.getPosition()));
+            returnList.add(returnedElement);
+        }
+        return returnList;
+    }
 }
 
diff --git a/src/java/org/apache/fop/layoutmgr/InlineLevelLayoutManager.java b/src/java/org/apache/fop/layoutmgr/InlineLevelLayoutManager.java
new file mode 100644 (file)
index 0000000..767e901
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+package org.apache.fop.layoutmgr;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * The interface for LayoutManagers which generate inline areas
+ */
+public interface InlineLevelLayoutManager extends LayoutManager {
+
+    /**
+     * Get a sequence of KnuthElements representing the content 
+     * of the node assigned to the LM
+     * 
+     * @param context   the LayoutContext used to store layout information
+     * @param alignment the desired text alignement
+     * @return          the list of KnuthElements
+     */
+    LinkedList getNextKnuthElements(LayoutContext context, int alignment);
+
+    /**
+     * Tell the LM to modify its data, adding a letter space 
+     * to the word fragment represented by the given element,
+     * and returning a corrected element
+     *
+     * @param element the element which must be given one more letter space
+     * @return        the new element replacing the old one
+     */
+    KnuthElement addALetterSpaceTo(KnuthElement element);
+
+    /**
+     * Get the word chars corresponding to the given position
+     *
+     * @param sbChars the StringBuffer used to append word chars
+     * @param pos     the Position referring to the needed word chars
+     */
+    void getWordChars(StringBuffer sbChars, Position pos);
+
+    /**
+     * Tell the LM to hyphenate a word
+     *
+     * @param pos the Position referring to the word
+     * @param hc  the HyphContext storing hyphenation information
+     */
+    void hyphenate(Position pos, HyphContext hc);
+
+    /**
+     * Tell the LM to apply the changes due to hyphenation
+     *
+     * @param oldList the list of the old elements the changes refer to
+     * @return        true if the LM had to change its data, false otherwise
+     */
+    boolean applyChanges(List oldList);
+
+    /**
+     * Get a sequence of KnuthElements representing the content 
+     * of the node assigned to the LM, after changes have been applied
+     *
+     * @param oldList        the elements to replace
+     * @param flaggedPenalty the penalty value for hyphenated lines
+     * @param alignment      the desired text alignment
+     * @return               the updated list of KnuthElements
+     */
+    LinkedList getChangedKnuthElements(List oldList, int flaggedPenalty,
+                                       int alignment);
+}
index e45e6be5553392cafe337204ed0f8f624843cd34..5e33d1336f97e3e9075786a3259dc92a4a65abd4 100644 (file)
@@ -69,7 +69,7 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager {
     private Area currentArea; // LineArea or InlineParent
 
     private BreakPoss prevBP;
-    private LayoutContext childLC ;
+    protected LayoutContext childLC ;
 
     private LayoutManager lastChildLM = null; // Set when return last breakposs
     private boolean bAreaCreated = false;
@@ -462,8 +462,9 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager {
             = new StackingIter(positionList.listIterator());
 
         LayoutManager prevLM = null;
-        LayoutManager childLM ;
-        while ((childLM = childPosIter.getNextChildLM()) != null) {
+        InlineLevelLayoutManager childLM ;
+        while ((childLM = (InlineLevelLayoutManager) childPosIter.getNextChildLM())
+               != null) {
             getContext().setFlags(LayoutContext.LAST_AREA,
                                   context.isLastArea() && childLM == lastLM);
             childLM.addAreas(childPosIter, getContext());
@@ -557,211 +558,5 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager {
             }
         }
     }
-
-    public LinkedList getNextKnuthElements(LayoutContext lc, int alignment) {
-        LayoutManager curLM;
-
-        // the list returned by child LM
-        LinkedList returnedList;
-        KnuthElement returnedElement;
-
-        // the list which will be returned to the parent LM
-        LinkedList returnList = new LinkedList();
-
-        SpaceSpecifier leadingSpace = lc.getLeadingSpace();
-
-        if (lc.startsNewArea()) {
-            // First call to this LM in new parent "area", but this may
-            // not be the first area created by this inline
-            childLC = new LayoutContext(lc);
-            if (getSpaceStart() != null) {
-                lc.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart()));
-            }
-
-            // Check for "fence"
-            if (hasLeadingFence(!lc.isFirstArea())) {
-                // Reset leading space sequence for child areas
-                leadingSpace = new SpaceSpecifier(false);
-            }
-            // Reset state variables
-            clearPrevIPD(); // Clear stored prev content dimensions
-        }
-
-        while ((curLM = getChildLM()) != null) {
-            // get KnuthElements from curLM
-            returnedList = curLM.getNextKnuthElements(lc, alignment);
-            if (returnedList != null) {
-                // "wrap" the Position stored in each element of returnedList
-                ListIterator listIter = returnedList.listIterator();
-                while (listIter.hasNext()) {
-                    returnedElement = (KnuthElement) listIter.next();
-                    returnedElement.setPosition
-                        (new NonLeafPosition(this,
-                                             returnedElement.getPosition()));
-                    returnList.add(returnedElement);
-                }
-                return returnList;
-            } else {
-                // curLM returned null because it finished;
-                // just iterate once more to see if there is another child
-            }
-        }
-        setFinished(true);
-        return null;
-    }
-
-    public KnuthElement addALetterSpaceTo(KnuthElement element) {
-        NonLeafPosition savedPos = (NonLeafPosition) element.getPosition();
-        element.setPosition(savedPos.getPosition());
-
-        KnuthElement newElement
-            = element.getLayoutManager().addALetterSpaceTo(element);
-        newElement.setPosition
-            (new NonLeafPosition(this, newElement.getPosition()));
-        element.setPosition(savedPos);
-        return newElement;
-    }
-
-    public void getWordChars(StringBuffer sbChars, Position pos) {
-        Position newPos = ((NonLeafPosition) pos).getPosition();
-        newPos.getLM().getWordChars(sbChars, newPos);
-    }
-
-    public void hyphenate(Position pos, HyphContext hc) {
-        Position newPos = ((NonLeafPosition) pos).getPosition();
-        newPos.getLM().hyphenate(newPos, hc);
-    }
-
-    public boolean applyChanges(List oldList) {
-        // "unwrap" the Positions stored in the elements
-        ListIterator oldListIterator = oldList.listIterator();
-        KnuthElement oldElement;
-        while (oldListIterator.hasNext()) {
-            oldElement = (KnuthElement) oldListIterator.next();
-            oldElement.setPosition
-                (((NonLeafPosition) oldElement.getPosition()).getPosition());
-        }
-        // reset the iterator
-        oldListIterator = oldList.listIterator();
-
-        LayoutManager prevLM = null;
-        LayoutManager currLM;
-        int fromIndex = 0;
-
-        boolean bSomethingChanged = false;
-        while(oldListIterator.hasNext()) {
-            oldElement = (KnuthElement) oldListIterator.next();
-            currLM = oldElement.getLayoutManager();
-            // initialize prevLM
-            if (prevLM == null) {
-                prevLM = currLM;
-            }
-
-            if (currLM != prevLM || !oldListIterator.hasNext()) {
-                if (oldListIterator.hasNext()) {
-                    bSomethingChanged
-                        = prevLM.applyChanges(oldList.subList(fromIndex, oldListIterator.previousIndex()))
-                        || bSomethingChanged;
-                    prevLM = currLM;
-                    fromIndex = oldListIterator.previousIndex();
-                } else if (currLM == prevLM) {
-                    bSomethingChanged
-                        = prevLM.applyChanges(oldList.subList(fromIndex, oldList.size()))
-                        || bSomethingChanged;
-                } else {
-                    bSomethingChanged
-                        = prevLM.applyChanges(oldList.subList(fromIndex, oldListIterator.previousIndex()))
-                        || bSomethingChanged;
-                    bSomethingChanged
-                        = currLM.applyChanges(oldList.subList(oldListIterator.previousIndex(), oldList.size()))
-                        || bSomethingChanged;
-                }
-            }
-        }
-
-        // "wrap" again the Positions stored in the elements
-        oldListIterator = oldList.listIterator();
-        while (oldListIterator.hasNext()) {
-            oldElement = (KnuthElement) oldListIterator.next();
-            oldElement.setPosition
-                (new NonLeafPosition(this, oldElement.getPosition()));
-        }
-        return bSomethingChanged;
-    }
-
-    public LinkedList getChangedKnuthElements(List oldList, int flaggedPenalty, int alignment) {
-        // "unwrap" the Positions stored in the elements
-        ListIterator oldListIterator = oldList.listIterator();
-        KnuthElement oldElement;
-        while (oldListIterator.hasNext()) {
-            oldElement = (KnuthElement) oldListIterator.next();
-            oldElement.setPosition
-                (((NonLeafPosition) oldElement.getPosition()).getPosition());
-        }
-        // reset the iterator
-        oldListIterator = oldList.listIterator();
-
-        KnuthElement returnedElement;
-        LinkedList returnedList = new LinkedList();
-        LinkedList returnList = new LinkedList();
-        LayoutManager prevLM = null;
-        LayoutManager currLM;
-        int fromIndex = 0;
-
-        while(oldListIterator.hasNext()) {
-            oldElement = (KnuthElement) oldListIterator.next();
-            currLM = oldElement.getLayoutManager();
-            if (prevLM == null) {
-                prevLM = currLM;
-            }
-
-            if (currLM != prevLM || !oldListIterator.hasNext()) {
-                if (oldListIterator.hasNext()) {
-                    returnedList.addAll
-                        (prevLM.getChangedKnuthElements
-                         (oldList.subList(fromIndex,
-                                          oldListIterator.previousIndex()),
-                          flaggedPenalty, alignment));
-                    prevLM = currLM;
-                    fromIndex = oldListIterator.previousIndex();
-                } else if (currLM == prevLM) {
-                    returnedList.addAll
-                        (prevLM.getChangedKnuthElements
-                         (oldList.subList(fromIndex, oldList.size()),
-                          flaggedPenalty, alignment));
-                } else {
-                    returnedList.addAll
-                        (prevLM.getChangedKnuthElements
-                         (oldList.subList(fromIndex,
-                                          oldListIterator.previousIndex()),
-                          flaggedPenalty, alignment));
-                    returnedList.addAll
-                        (currLM.getChangedKnuthElements
-                         (oldList.subList(oldListIterator.previousIndex(),
-                                          oldList.size()),
-                          flaggedPenalty, alignment));
-                }
-            }
-        }
-
-        // "wrap" the Position stored in each element of returnedList
-        ListIterator listIter = returnedList.listIterator();
-        while (listIter.hasNext()) {
-            returnedElement = (KnuthElement) listIter.next();
-            returnedElement.setPosition
-                (new NonLeafPosition(this, returnedElement.getPosition()));
-            returnList.add(returnedElement);
-        }
-        return returnList;
-    }
-
-    public int getWordSpaceIPD() {
-        LayoutManager firstChild = getChildLM();
-        if (firstChild != null) {
-            return firstChild.getWordSpaceIPD();
-        } else {
-            return 0;
-        }
-    }
 }
 
index 5cbcb132e899880e178b354b670964c49d52f22e..a6f41d5c3dcbf527aaa7b9e798e720cedfadfbaf 100644 (file)
@@ -86,6 +86,7 @@ public class LayoutContext {
 
     private int iLineHeight;
     private int iBaseline;
+    private int iMiddleShift;
 
     public LayoutContext(LayoutContext parentLC) {
         this.flags = parentLC.flags;
@@ -98,6 +99,7 @@ public class LayoutContext {
         this.ipdAdjust = parentLC.ipdAdjust;
         this.iLineHeight = parentLC.iLineHeight;
         this.iBaseline = parentLC.iBaseline;
+        this.iMiddleShift = parentLC.iMiddleShift;
         // Copy other fields as necessary. Use clone???
     }
 
@@ -225,6 +227,14 @@ public class LayoutContext {
         return iBaseline;
     }
     
+    public void setMiddleShift(int ms) {
+        iMiddleShift = ms;
+    }
+
+    public int getMiddleBaseline() {
+        return iBaseline + iMiddleShift;
+    }
+    
     public String toString() {
         return "Layout Context:" +
         "\nStack Limit: \t" + (getStackLimit() == null ? "null" : getStackLimit().toString()) +
@@ -235,6 +245,7 @@ public class LayoutContext {
         "\nIPD Adjust: \t" + getIPDAdjust() +
         "\nLine Height: \t" + getLineHeight() +
         "\nBaseline: \t" + getBaseline() +
+        "\nMiddle Baseline: \t" + getMiddleBaseline() +
         "\nResolve Leading Space: \t" + resolveLeadingSpace() +
         "\nSuppress Leading Space: \t" + suppressLeadingSpace() +
         "\nIs First Area: \t" + isFirstArea() + 
index 56c059514dde4a8f1803855548fa91f4c2ab0eb7..6e4d736ab268903556b9e0c5438bc5ce86635e6e 100644 (file)
@@ -237,19 +237,4 @@ public interface LayoutManager {
      * @param newLMs the list of LMs to be added
      */
     void addChildLMs(List newLMs);
-
-    LinkedList getNextKnuthElements(LayoutContext context, int alignment);
-
-    KnuthElement addALetterSpaceTo(KnuthElement element);
-
-    void getWordChars(StringBuffer sbChars, Position pos);
-
-    void hyphenate(Position pos, HyphContext hc);
-
-    boolean applyChanges(List oldList);
-
-    LinkedList getChangedKnuthElements(List oldList, int flaggedPenalty,
-                                       int alignment);
-
-    int getWordSpaceIPD();
 }
index f7bc3f39358fdf27e416bb17c5a0249791a51c71..836783c71fba519ea07f248284d4f4481ae6da60 100644 (file)
@@ -23,7 +23,10 @@ import org.apache.fop.area.inline.FilledArea;
 import org.apache.fop.area.inline.InlineArea;
 import org.apache.fop.area.inline.Space;
 import org.apache.fop.area.inline.TextArea;
+import org.apache.fop.datatypes.Length;
 import org.apache.fop.datatypes.PercentBase;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.flow.Inline;
 import org.apache.fop.fo.flow.Leader;
 import org.apache.fop.fonts.Font;
 import org.apache.fop.traits.MinOptMax;
@@ -38,6 +41,9 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
     private Leader fobj;
     Font font = null;
     
+    private LinkedList contentList = null;
+    private ContentLayoutManager clm = null;
+
     /**
      * Constructor
      *
@@ -48,7 +54,10 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
         super(node);
         fobj = node;
         font = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo());
-        setAlignment(node.getLeaderAlignment());
+        // the property leader-alignment does not affect vertical positioning
+        // (see section 7.21.1 in the XSL Recommendation)
+        // setAlignment(node.getLeaderAlignment());
+        setAlignment(fobj.getVerticalAlign());
     }
 
     public InlineArea get(LayoutContext context) {
@@ -84,10 +93,9 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
             char dot = '.'; // userAgent.getLeaderDotCharacter();
 
             t.setTextArea("" + dot);
+            t.setIPD(font.getCharWidth(dot));
             t.addTrait(Trait.FONT_NAME, font.getFontName());
             t.addTrait(Trait.FONT_SIZE, new Integer(font.getFontSize()));
-            // set offset of dot within inline parent
-            t.setOffset(font.getAscender());
             int width = font.getCharWidth(dot);
             Space spacer = null;
             if (fobj.getLeaderPatternWidth().getValue() > width) {
@@ -116,15 +124,15 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
             // get breaks then add areas to FilledArea
             FilledArea fa = new FilledArea();
 
-            ContentLayoutManager clm = new ContentLayoutManager(fa);
+            clm = new ContentLayoutManager(fa);
             clm.setUserAgent(fobj.getUserAgent());
             addChildLM(clm);
 
-            InlineStackingLayoutManager lm;
-            lm = new InlineStackingLayoutManager(fobj);
+            InlineLayoutManager lm;
+            lm = new InlineLayoutManager(fobj);
             clm.addChildLM(lm);
 
-            clm.fillArea(lm);
+            contentList = clm.getNextKnuthElements(new LayoutContext(0), 0);
             int width = clm.getStackingSize();
             Space spacer = null;
             if (fobj.getLeaderPatternWidth().getValue() > width) {
@@ -141,6 +149,90 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
         return leaderArea;
      }
 
+    protected void offsetArea(LayoutContext context) {
+        int pattern = fobj.getLeaderPattern();
+        int bpd = curArea.getBPD();
+
+        switch (pattern) {
+            case LeaderPattern.RULE: 
+                switch (verticalAlignment) {
+                    case VerticalAlign.TOP:
+                        curArea.setOffset(0);
+                    break;
+                    case VerticalAlign.MIDDLE:
+                        curArea.setOffset(context.getMiddleBaseline() - bpd / 2);
+                    break;
+                    case VerticalAlign.BOTTOM:
+                        curArea.setOffset(context.getLineHeight() - bpd);
+                    break;
+                    case VerticalAlign.BASELINE: // fall through
+                    default:
+                        curArea.setOffset(context.getBaseline() - bpd);
+                    break;
+                }
+            break;
+            case LeaderPattern.DOTS: 
+                switch (verticalAlignment) {
+                    case VerticalAlign.TOP:
+                        curArea.setOffset(0);
+                    break;
+                    case VerticalAlign.MIDDLE:
+                        curArea.setOffset(context.getMiddleBaseline());
+                    break;
+                    case VerticalAlign.BOTTOM:
+                        curArea.setOffset(context.getLineHeight() - bpd + font.getAscender());
+                    break;
+                    case VerticalAlign.BASELINE: // fall through
+                    default:
+                        curArea.setOffset(context.getBaseline());
+                    break;
+                }
+            break;
+            case LeaderPattern.SPACE: 
+                // nothing to do
+            break;
+            case LeaderPattern.USECONTENT: 
+                switch (verticalAlignment) {
+                    case VerticalAlign.TOP:
+                        curArea.setOffset(0);
+                    break;
+                    case VerticalAlign.MIDDLE:
+                        curArea.setOffset(context.getMiddleBaseline());
+                    break;
+                    case VerticalAlign.BOTTOM:
+                        curArea.setOffset(context.getLineHeight() - bpd);
+                    break;
+                    case VerticalAlign.BASELINE: // fall through
+                    default:
+                        curArea.setOffset(context.getBaseline());
+                    break;
+                }
+            break;
+        }
+    }
+
+    public void addAreas(PositionIterator posIter, LayoutContext context) {
+        if (fobj.getLeaderPattern() != LeaderPattern.USECONTENT) {
+            // use LeafNodeLayoutManager.addAreas()
+            super.addAreas(posIter, context);
+        } else {
+            addId();
+
+            widthAdjustArea(context);
+
+            // add content areas
+            KnuthPossPosIter contentIter = new KnuthPossPosIter(contentList, 0, contentList.size());
+            clm.addAreas(contentIter, context);
+            offsetArea(context);
+
+            parentLM.addChild(curArea);
+
+            while (posIter.hasNext()) {
+                posIter.next();
+            }
+        }
+    }
+
     public LinkedList getNextKnuthElements(LayoutContext context,
                                            int alignment) {
         MinOptMax ipd;
@@ -158,15 +250,13 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
         int lead = 0;
         int total = 0;
         int middle = 0;
-        switch (alignment) {
+        switch (verticalAlignment) {
             case VerticalAlign.MIDDLE  : middle = bpd / 2 ;
-                                         lead = bpd / 2 ;
-                                         break;
-            case VerticalAlign.TOP     : total = bpd;
                                          break;
+            case VerticalAlign.TOP     : // fall through
             case VerticalAlign.BOTTOM  : total = bpd;
                                          break;
-            case VerticalAlign.BASELINE:
+            case VerticalAlign.BASELINE: // fall through
             default:                     lead = bpd;
                                          break;
         }
index 42da5776f95668a1e6953667ecc0d7f835c22ae5..ce1a19aafd90c2bfc88bf6391ec741563cf2975a 100644 (file)
@@ -33,12 +33,13 @@ import java.util.LinkedList;
  * This class can be extended to handle the creation and adding of the
  * inline area.
  */
-public class LeafNodeLayoutManager extends AbstractLayoutManager {
+public class LeafNodeLayoutManager extends AbstractLayoutManager 
+                                   implements InlineLevelLayoutManager {
     /**
      * The inline area that this leafnode will add.
      */
     protected InlineArea curArea = null;
-    protected int alignment;
+    protected int verticalAlignment;
     private int lead;
     private MinOptMax ipd;
 
@@ -116,7 +117,7 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager {
      * @param al the vertical alignment positioning
      */
     public void setAlignment(int al) {
-        alignment = al;
+        verticalAlignment = al;
     }
 
     /**
@@ -146,49 +147,6 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager {
         return null;
     }
 
-    /**
-     * Get the next break position.
-     * Since this holds an inline area it will return a single
-     * break position.
-     * @param context the layout context for this inline area
-     * @return the break poisition for adding this inline area
-     */
-    public BreakPoss getNextBreakPoss(LayoutContext context) {
-        curArea = get(context);
-        if (curArea == null) {
-            setFinished(true);
-            return null;
-        }
-        BreakPoss bp = new BreakPoss(new LeafPosition(this, 0),
-                                     BreakPoss.CAN_BREAK_AFTER
-                                     | BreakPoss.CAN_BREAK_BEFORE | BreakPoss.ISFIRST
-                                     | BreakPoss.ISLAST);
-        ipd = getAllocationIPD(context.getRefIPD());
-        bp.setStackingSize(ipd);
-        bp.setNonStackingSize(new MinOptMax(curArea.getBPD()));
-        bp.setTrailingSpace(new SpaceSpecifier(false));
-
-        int bpd = curArea.getBPD();
-        switch (alignment) {
-            case VerticalAlign.MIDDLE:
-                bp.setMiddle(bpd / 2 /* - fontLead/2 */);
-                bp.setLead(bpd / 2 /* + fontLead/2 */);
-            break;
-            case VerticalAlign.TOP:
-                bp.setTotal(bpd);
-            break;
-            case VerticalAlign.BOTTOM:
-                bp.setTotal(bpd);
-            break;
-            case VerticalAlign.BASELINE:
-            default:
-                bp.setLead(bpd);
-            break;
-        }
-        setFinished(true);
-        return bp;
-    }
-
     /**
      * Get the allocation ipd of the inline area.
      * This method may be overridden to handle percentage values.
@@ -199,19 +157,6 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager {
         return new MinOptMax(curArea.getIPD());
     }
 
-    /**
-     * Reset the position.
-     * If the reset position is null then this inline area should be
-     * restarted.
-     * @param resetPos the position to reset.
-     */
-    public void resetPosition(Position resetPos) {
-        // only reset if setting null, start again
-        if (resetPos == null) {
-            setFinished(false);
-        }
-    }
-
     /**
      * Add the area for this layout manager.
      * This adds the single inline area to the parent.
@@ -244,9 +189,9 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager {
      */
     protected void offsetArea(LayoutContext context) {
         int bpd = curArea.getBPD();
-        switch (alignment) {
+        switch (verticalAlignment) {
             case VerticalAlign.MIDDLE:
-                curArea.setOffset(context.getBaseline() - bpd / 2 /* - fontLead/2 */);
+                curArea.setOffset(context.getMiddleBaseline() - bpd / 2);
             break;
             case VerticalAlign.TOP:
                 //curArea.setOffset(0);
@@ -305,7 +250,7 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager {
         int lead = 0;
         int total = 0;
         int middle = 0;
-        switch (alignment) {
+        switch (verticalAlignment) {
             case VerticalAlign.MIDDLE  : middle = bpd / 2 ;
                                          lead = bpd / 2 ;
                                          break;
@@ -331,6 +276,9 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager {
         return returnList;
     }
 
+    public void getWordChars(StringBuffer sbChars, Position bp) {
+    }
+
     public KnuthElement addALetterSpaceTo(KnuthElement element) {
         // return the unchanged box object
         return new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
@@ -339,8 +287,6 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager {
     }
 
     public void hyphenate(Position pos, HyphContext hc) {
-        // use the AbstractLayoutManager.hyphenate() null implementation
-        super.hyphenate(pos, hc);
     }
 
     public boolean applyChanges(List oldList) {
index d30b98f6aeeb72d9cfe72ba9eef907902afea3f7..0345cae8b981a7c095848ef622f8661a56b4dcd1 100644 (file)
@@ -58,7 +58,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
      * @param l the default lead, from top to baseline
      * @param f the default follow, from baseline to bottom
      */
-    public LineLayoutManager(Block node, int lh, int l, int f) {
+    public LineLayoutManager(Block node, int lh, int l, int f, int ms) {
         super(node);
         fobj = node;
         // the child FObj are owned by the parent BlockLM
@@ -67,6 +67,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
         lineHeight = lh;
         lead = l;
         follow = f;
+        middleShift = ms;
         initialize(); // Normally done when started by parent!
     }
 
@@ -126,6 +127,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
     private int lineHeight;
     private int lead;
     private int follow;
+    // offset of the middle baseline with respect to the main baseline
+    private int middleShift;
 
     // inline start pos when adding areas
     private int iStartPos = 0;
@@ -151,6 +154,11 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
     // suggested modification to the "optimum" number of lines
     private int looseness = 0;
 
+    // this constant is used to create elements when text-align is center:
+    // every TextLM descendant of LineLM must use the same value, 
+    // otherwise the line breaking algorithm does not find the right
+    // break point
+    public static final int DEFAULT_SPACE_WIDTH = 3336;
     private static final int INFINITE_RATIO = 1000;
 
     // this class represent a feasible breaking point
@@ -272,10 +280,10 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
     // which was the first element in the paragraph
     // returned by each LM
     private class Update {
-        private LayoutManager inlineLM;
+        private InlineLevelLayoutManager inlineLM;
         private int iFirstIndex;
 
-        public Update(LayoutManager lm, int index) {
+        public Update(InlineLevelLayoutManager lm, int index) {
             inlineLM = lm;
             iFirstIndex = index;
         }
@@ -288,28 +296,19 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
         public int ignoreAtEnd = 0;
         // minimum space at the end of the last line (in millipoints)
         public int lineFillerWidth;
-        // word space dimension (in millipoints)
-        private int wordSpaceIPD;
 
         public void startParagraph(int lineWidth) {
-            // get the word space dimension, which needs to be known
-            // in order to center text
-            LayoutManager lm;
-            if ((lm = getChildLM()) != null) {
-                wordSpaceIPD = lm.getWordSpaceIPD();
-            }
-
             // set the minimum amount of empty space at the end of the
             // last line
             if (bTextAlignment == CENTER) {
                 lineFillerWidth = 0; 
             } else {
-                lineFillerWidth = (int)(lineWidth / 6); 
+                lineFillerWidth = (int)(lineWidth / 12); 
             }
 
             // add auxiliary elements at the beginning of the paragraph
             if (bTextAlignment == CENTER && bTextAlignmentLast != JUSTIFY) {
-                this.add(new KnuthGlue(0, 3 * wordSpaceIPD, 0,
+                this.add(new KnuthGlue(0, 3 * DEFAULT_SPACE_WIDTH, 0,
                                        null, false));
                 ignoreAtStart ++;
             }
@@ -333,7 +332,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
             if (this.size() > ignoreAtStart) {
                 if (bTextAlignment == CENTER
                     && bTextAlignmentLast != JUSTIFY) {
-                    this.add(new KnuthGlue(0, 3 * wordSpaceIPD, 0,
+                    this.add(new KnuthGlue(0, 3 * DEFAULT_SPACE_WIDTH, 0,
                                            null, false));
                     this.add(new KnuthPenalty(0, -KnuthElement.INFINITE,
                                               false, null, false));
@@ -371,7 +370,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
     public BreakPoss getNextBreakPoss(LayoutContext context) {
         // Get a break from currently active child LM
         // Set up constraints for inline level managers
-        LayoutManager curLM ; // currently active LM
+        InlineLevelLayoutManager curLM ; // currently active LM
         BreakPoss prev = null;
         BreakPoss bp = null; // proposed BreakPoss
 
@@ -407,57 +406,66 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
 
             Paragraph knuthPar = new Paragraph();
             knuthPar.startParagraph(availIPD.opt);
-            while ((curLM = getChildLM()) != null) {
+            while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) {
                 if ((returnedList
                      = curLM.getNextKnuthElements(inlineLC,
                                                   effectiveAlignment))
                     != null) {
-                    // if there are two consecutive KnuthBox, the first one 
-                    // does not represent a whole word, so it must be given
-                    // one more letter space
+                    // look at the first element
                     thisElement = (KnuthElement) returnedList.getFirst();
-                    if (returnedList.size() > 1
-                        || !(thisElement.isPenalty()
-                             && ((KnuthPenalty) thisElement).getP()
-                             == -KnuthElement.INFINITE)) {
-                        if (thisElement.isBox() && !thisElement.isAuxiliary()
-                            && bPrevWasKnuthBox) {
-                            prevBox = (KnuthBox) knuthPar.removeLast();
-                            if (!prevBox.isAuxiliary()) {
-                                // if letter spacing is constant,
-                                // only prevBox needs to be replaced;
-                                knuthPar.addLast(prevBox.getLayoutManager()
-                                                 .addALetterSpaceTo(prevBox));
-                            } else {
-                                // prevBox is the last element
-                                // in the sub-sequence
-                                //   <box> <aux penalty> <aux glue> <aux box>
-                                // the letter space is added to <aux glue>,
-                                // while the other elements are not changed
-                                KnuthBox auxBox = prevBox;
-                                KnuthGlue auxGlue
-                                    = (KnuthGlue) knuthPar.removeLast();
-                                KnuthPenalty auxPenalty
-                                    = (KnuthPenalty) knuthPar.removeLast();
-                                prevBox = (KnuthBox) knuthPar.getLast();
-                                knuthPar.addLast(auxPenalty);
-                                knuthPar.addLast(prevBox.getLayoutManager()
-                                                 .addALetterSpaceTo(prevBox));
-                                knuthPar.addLast(auxBox);
-                }
-                        }
-                        if (((KnuthElement) returnedList.getLast()).isBox()) {
-                            bPrevWasKnuthBox = true;
+                    if (thisElement.isBox() && !thisElement.isAuxiliary()
+                        && bPrevWasKnuthBox) {
+                        prevBox = (KnuthBox) knuthPar.removeLast();
+                        // if there are two consecutive KnuthBoxes the
+                        // first one does not represent a whole word,
+                        // so it must be given one more letter space
+                        if (!prevBox.isAuxiliary()) {
+                            // if letter spacing is constant,
+                            // only prevBox needs to be replaced;
+                            knuthPar.addLast(((InlineLevelLayoutManager)
+                                              prevBox.getLayoutManager())
+                                             .addALetterSpaceTo(prevBox));
                         } else {
-                            bPrevWasKnuthBox = false;
+                            // prevBox is the last element
+                            // in the sub-sequence
+                            //   <box> <aux penalty> <aux glue> <aux box>
+                            // the letter space is added to <aux glue>,
+                            // while the other elements are not changed
+                            KnuthBox auxBox = prevBox;
+                            KnuthGlue auxGlue
+                                = (KnuthGlue) knuthPar.removeLast();
+                            KnuthPenalty auxPenalty
+                                = (KnuthPenalty) knuthPar.removeLast();
+                            prevBox = (KnuthBox) knuthPar.getLast();
+                            knuthPar.addLast(auxPenalty);
+                            knuthPar.addLast(((InlineLevelLayoutManager)
+                                              prevBox.getLayoutManager())
+                                             .addALetterSpaceTo(prevBox));
+                            knuthPar.addLast(auxBox);
                         }
-                        // add the new elements to the paragraph
-                        knuthPar.addAll(returnedList);
+                    }
+
+                    // look at the last element
+                    KnuthElement lastElement = (KnuthElement) returnedList.getLast();
+                    boolean bForceLinefeed = false;
+                    if (lastElement.isBox()) {
+                        bPrevWasKnuthBox = true;
                     } else {
-                        // a list with a single penalty item
-                        // whose value is -inf
-                        // represents a preserved linefeed,
-                        // wich forces a line break
+                        bPrevWasKnuthBox = false;
+                        if (lastElement.isPenalty()
+                            && ((KnuthPenalty) lastElement).getP()
+                                == -KnuthPenalty.INFINITE) {
+                            // a penalty item whose value is -inf
+                            // represents a preserved linefeed,
+                            // wich forces a line break
+                            bForceLinefeed = true;
+                            returnedList.removeLast();
+                        }
+                    }
+
+                    // add the new elements to the paragraph
+                    knuthPar.addAll(returnedList);
+                    if (bForceLinefeed) {
                         knuthPar.endParagraph();
                         knuthPar = new Paragraph();
                         knuthPar.startParagraph(availIPD.opt);
@@ -467,7 +475,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
                     // curLM returned null; this can happen
                     // if it has nothing more to layout,
                     // so just iterate once more to see
-                    // if there are other chilren
+                    // if there are other children
                 }
             }
             knuthPar.endParagraph();
@@ -647,53 +655,11 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
             double ratio = (textAlign == JUSTIFY)
                 ? bestActiveNode.adjustRatio : 0;
 
-            // lead to baseline is
-            // max of: baseline fixed alignment and middle/2
-            // after baseline is
-            // max: top height-lead, middle/2 and bottom height-lead
-            int halfLeading = (lineHeight - lead - follow) / 2;
-            // height before baseline
-            int lineLead = lead + halfLeading;
-            // maximum size of top and bottom alignment
-            int maxtb = follow + halfLeading;
-            // max size of middle alignment below baseline
-            int middlefollow = maxtb;
-
-            // index of the first KnuthElement in this line
-            int firstElementIndex = 0;
-            if (line > 1) {
-                firstElementIndex = bestActiveNode.previous.position + 1;
-            }
-            ListIterator inlineIterator = par.listIterator(firstElementIndex);
-            for (int j = 0;
-                 j < (bestActiveNode.position - firstElementIndex + 1);
-                 j++) {
-                KnuthElement element = (KnuthElement) inlineIterator.next();
-                if (element.isBox()) {
-                    if (((KnuthBox) element).getLead() > lineLead) {
-                        lineLead = ((KnuthBox) element).getLead();
-                    }
-                    if (((KnuthBox) element).getTotal() > maxtb) {
-                        maxtb = ((KnuthBox) element).getTotal();
-                    }
-                    if (((KnuthBox) element).getMiddle() > middlefollow) {
-                        middlefollow = ((KnuthBox) element).getMiddle();
-                    }
-                }
-            }
-
-            if (maxtb - lineLead > middlefollow) {
-                middlefollow = maxtb - lineLead;
-            }
-
-            // add nodes at the beginning of the list, as they are found
-            // backwards, from the last one to the first one
-            breakpoints.add(0,
-                            new LineBreakPosition(this,
+            makeLineBreakPosition(par,
+                                  (i > 1 ? bestActiveNode.previous.position + 1: 0),
                                                   bestActiveNode.position,
-                                                  ratio, 0, indent,
-                                                  lineLead + middlefollow,
-                                                  lineLead));
+                                  0, ratio, indent);
+
             bestActiveNode = bestActiveNode.previous;
         }
         if (bForced) {
@@ -705,22 +671,29 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
     }
 
     private void fallback(Paragraph par, int line) {
-        // lead to baseline is
-        // max of: baseline fixed alignment and middle/2
-        // after baseline is
-        // max: top height-lead, middle/2 and bottom height-lead
+        makeLineBreakPosition(par,
+                              lastDeactivatedNode.position,
+                              par.size() - 1,
+                              line, 0, 0);
+    }
+
+    private void makeLineBreakPosition(Paragraph par,
+                                       int firstElementIndex, int lastElementIndex,
+                                       int insertIndex, double ratio, int indent) {
+        // line height calculation
+
         int halfLeading = (lineHeight - lead - follow) / 2;
-        // height before baseline
+        // height above the main baseline
         int lineLead = lead + halfLeading;
         // maximum size of top and bottom alignment
         int maxtb = follow + halfLeading;
-        // max size of middle alignment below baseline
+        // max size of middle alignment above and below the middle baseline
         int middlefollow = maxtb;
 
         ListIterator inlineIterator
-            = par.listIterator(lastDeactivatedNode.position);
-        for (int j = lastDeactivatedNode.position;
-             j < (par.size());
+            = par.listIterator(firstElementIndex);
+        for (int j = firstElementIndex;
+             j <= lastElementIndex;
              j++) {
             KnuthElement element = (KnuthElement) inlineIterator.next();
             if (element.isBox()) {
@@ -730,8 +703,13 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
                 if (((KnuthBox) element).getTotal() > maxtb) {
                     maxtb = ((KnuthBox) element).getTotal();
                 }
-                if (((KnuthBox) element).getMiddle() > middlefollow) {
-                    middlefollow = ((KnuthBox) element).getMiddle();
+                if (((KnuthBox) element).getMiddle() > lineLead + middleShift) {
+                    lineLead += ((KnuthBox) element).getMiddle()
+                                - lineLead - middleShift;
+                }
+                if (((KnuthBox) element).getMiddle() > middlefollow - middleShift) {
+                    middlefollow += ((KnuthBox) element).getMiddle()
+                                    - middlefollow + middleShift;
                 }
             }
         }
@@ -740,14 +718,14 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
                     middlefollow = maxtb - lineLead;
         }
 
-        breakpoints.add(line,
-                        new LineBreakPosition(this, par.size() - 1,
-                                              0, 0, 0,
+        breakpoints.add(insertIndex,
+                        new LineBreakPosition(this,
+                                              lastElementIndex ,
+                                              ratio, 0, indent,
                                               lineLead + middlefollow,
                                               lineLead));
     }
 
-
     private void considerLegalBreak(LinkedList par, int lineWidth,
                                     KnuthElement element,
                                     int totalWidth, int totalStretch,
@@ -969,8 +947,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
         LinkedList updateList = new LinkedList();
         KnuthElement firstElement = null;
         KnuthElement nextElement = null;
-        // current TextLayoutManager
-        LayoutManager currLM = null;
+        // current InlineLevelLayoutManager
+        InlineLevelLayoutManager currLM = null;
         // number of KnuthBox elements containing word fragments
         int boxCount;
         // number of auxiliary KnuthElements between KnuthBoxes
@@ -982,7 +960,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
             firstElement = (KnuthElement) currParIterator.next();
             // 
             if (firstElement.getLayoutManager() != currLM) {
-                currLM = firstElement.getLayoutManager();
+                currLM = (InlineLevelLayoutManager) firstElement.getLayoutManager();
                 if (currLM != null) { 
                     updateList.add(new Update(currLM, currParIterator.previousIndex()));
                 } else {
@@ -1003,7 +981,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
                     if (nextElement.isBox() && !nextElement.isAuxiliary()) {
                         // a non-auxiliary KnuthBox: append word chars
                         if (currLM != nextElement.getLayoutManager()) {
-                            currLM = nextElement.getLayoutManager();
+                            currLM = (InlineLevelLayoutManager) nextElement.getLayoutManager();
                             updateList.add(new Update(currLM, currParIterator.previousIndex()));
                         }
                         // append text to recreate the whole word
@@ -1031,7 +1009,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
                     for (int i = 0; i < (boxCount + auxCount); i++) {
                         element = (KnuthElement) currParIterator.next();
                         if (element.isBox() && !element.isAuxiliary()) {
-                            element.getLayoutManager().hyphenate(element.getPosition(), hc);
+                            ((InlineLevelLayoutManager)
+                             element.getLayoutManager()).hyphenate(element.getPosition(), hc);
                         } else {
                             // nothing to do, element is an auxiliary KnuthElement
                         }
@@ -1065,7 +1044,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
 
             // applyChanges() returns true if the LM modifies its data,
             // so it must return new KnuthElements to replace the old ones
-            if (currUpdate.inlineLM
+            if (((InlineLevelLayoutManager) currUpdate.inlineLM)
                 .applyChanges(currPar.subList(fromIndex + iAddedElements,
                                               toIndex + iAddedElements))) {
                 // insert the new KnuthElements
@@ -1415,6 +1394,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
             lineArea.setBPD(lbp.lineHeight);
             lc.setBaseline(lbp.baseline);
             lc.setLineHeight(lbp.lineHeight);
+            lc.setMiddleShift(middleShift);
             setCurrentArea(lineArea);
 
             Paragraph currPar = (Paragraph) knuthParagraphs.get(iCurrParIndex);
index d92f6ec2ab10a7a431bfba3484db888b625d9b35..2b4e673a1c03cab7aa7864ae4a8e0728cc085dab 100644 (file)
@@ -73,7 +73,8 @@ public class RetrieveMarkerLayoutManager extends AbstractLayoutManager {
         if (replaceLM == null) {
             return null;
         }
-        return replaceLM.getNextKnuthElements(context, alignment);
+        return ((InlineLevelLayoutManager) replaceLM)
+               .getNextKnuthElements(context, alignment);
     }
 
     public void addAreas(PositionIterator parentIter,
index b6cb0c35348ba3afc4730228fb23023d9893de92..706849025a6c781d366eadd94ddf7ca62be6385a 100644 (file)
@@ -24,6 +24,7 @@ import java.util.LinkedList;
 import java.util.ListIterator;
 
 import org.apache.fop.fo.FOText;
+import org.apache.fop.fo.flow.Inline;
 import org.apache.fop.fonts.Font;
 import org.apache.fop.traits.SpaceVal;
 import org.apache.fop.area.Trait;
@@ -36,7 +37,8 @@ import org.apache.fop.traits.MinOptMax;
  * LayoutManager for text (a sequence of characters) which generates one
  * or more inline areas.
  */
-public class TextLayoutManager extends AbstractLayoutManager {
+public class TextLayoutManager extends AbstractLayoutManager
+                               implements InlineLevelLayoutManager {
 
     /**
      * Store information about each potential text area.
@@ -117,6 +119,12 @@ public class TextLayoutManager extends AbstractLayoutManager {
     private short iTempStart = 0;
     private LinkedList changeList = null;
 
+    private int textHeight;
+    private int lead = 0;
+    private int total = 0;
+    private int middle = 0;
+    private int verticalAlignment = VerticalAlign.BASELINE;
+
     /**
      * Create a Text layout manager.
      *
@@ -155,6 +163,26 @@ public class TextLayoutManager extends AbstractLayoutManager {
         // in the SpaceVal.makeWordSpacing() method
         letterSpaceIPD = ls.getSpace();
         wordSpaceIPD = MinOptMax.add(new MinOptMax(spaceCharIPD), ws.getSpace());
+
+        // set text height
+        textHeight = fs.getAscender()
+                     - fs.getDescender();
+
+        // if the text node is son of an inline, set vertical align
+        if (foText.getParent() instanceof Inline) {
+            setAlignment(((Inline) foText.getParent()).getVerticalAlign());
+        }
+        switch (verticalAlignment) {
+            case VerticalAlign.MIDDLE  : middle = textHeight / 2 ;
+                                         break;
+            case VerticalAlign.TOP     : // fall through
+            case VerticalAlign.BOTTOM  : total = textHeight;
+                                         break;
+            case VerticalAlign.BASELINE: // fall through
+            default                    : lead = fs.getAscender();
+                                         total = textHeight;
+                                         break;
+        }
     }
 
     /**
@@ -458,8 +486,8 @@ public class TextLayoutManager extends AbstractLayoutManager {
          * used for calculating the bpd of the line area containing
          * this text.
          */
-        //bp.setDescender(foText.textInfo.fs.getDescender());
-        //bp.setAscender(foText.textInfo.fs.getAscender());
+        //bp.setDescender(fs.getDescender());
+        //bp.setAscender(fs.getAscender());
         if (iNextStart == textArray.length) {
             flags |= BreakPoss.ISLAST;
             setFinished(true);
@@ -574,7 +602,7 @@ public class TextLayoutManager extends AbstractLayoutManager {
         iTotalAdjust += (iWordSpaceDim - wordSpaceIPD.opt) * iWScount;
 
         TextArea t = createTextArea(str, realWidth.opt + iTotalAdjust,
-                                    context.getBaseline());
+                                    context);
 
         // iWordSpaceDim is computed in relation to wordSpaceIPD.opt
         // but the renderer needs to know the adjustment in relation
@@ -606,12 +634,26 @@ public class TextLayoutManager extends AbstractLayoutManager {
      * @param base the baseline position
      * @return the new word area
      */
-    protected TextArea createTextArea(String str, int width, int base) {
+    protected TextArea createTextArea(String str, int width, LayoutContext context) {
         TextArea textArea = new TextArea();
         textArea.setIPD(width);
         textArea.setBPD(fs.getAscender() - fs.getDescender());
-        textArea.setOffset(fs.getAscender());
-        textArea.setOffset(base);
+        int bpd = textArea.getBPD();
+        switch (verticalAlignment) {
+            case VerticalAlign.MIDDLE:
+                textArea.setOffset(context.getMiddleBaseline() + fs.getXHeight() / 2);
+            break;
+            case VerticalAlign.TOP:
+                textArea.setOffset(fs.getAscender());
+            break;
+            case VerticalAlign.BOTTOM:
+                textArea.setOffset(context.getLineHeight() - bpd + fs.getAscender());
+            break;
+            case VerticalAlign.BASELINE:
+            default:
+                textArea.setOffset(context.getBaseline());
+            break;
+        }
 
         textArea.setTextArea(str);
         textArea.addTrait(Trait.FONT_NAME, fs.getFontName());
@@ -620,13 +662,29 @@ public class TextLayoutManager extends AbstractLayoutManager {
         return textArea;
     }
 
+    /**
+     * Set the alignment of the inline area.
+     * @param al the vertical alignment positioning
+     */
+    public void setAlignment(int al) {
+        verticalAlignment = al;
+    }
+
     public LinkedList getNextKnuthElements(LayoutContext context,
                                            int alignment) {
         LinkedList returnList = new LinkedList();
 
         while (iNextStart < textArray.length) {
-            if (textArray[iNextStart] == SPACE) {
+            if (textArray[iNextStart] == SPACE
+                || textArray[iNextStart] == NBSPACE) {
                 // normal, breaking space
+                // or non-breaking space
+                if (textArray[iNextStart] == NBSPACE) {
+                    returnList.add
+                        (new KnuthPenalty(0, KnuthElement.INFINITE, false,
+                                          new LeafPosition(this, vecAreaInfo.size() - 1),
+                                          false));
+                }
                 switch (alignment) {
                 case CENTER :
                     vecAreaInfo.add
@@ -634,14 +692,14 @@ public class TextLayoutManager extends AbstractLayoutManager {
                                       (short) 1, (short) 0,
                                       wordSpaceIPD, false));
                     returnList.add
-                        (new KnuthGlue(0, 3 * wordSpaceIPD.opt, 0,
+                        (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
                                        new LeafPosition(this, vecAreaInfo.size() - 1), false));
                     returnList.add
                         (new KnuthPenalty(0, 0, false,
                                           new LeafPosition(this, -1), true));
                     returnList.add
                         (new KnuthGlue(wordSpaceIPD.opt,
-                                       - 6 * wordSpaceIPD.opt, 0,
+                                       - 6 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
                                        new LeafPosition(this, -1), true));
                     returnList.add
                         (new KnuthBox(0, 0, 0, 0,
@@ -650,7 +708,7 @@ public class TextLayoutManager extends AbstractLayoutManager {
                         (new KnuthPenalty(0, KnuthElement.INFINITE, false,
                                           new LeafPosition(this, -1), true));
                     returnList.add
-                        (new KnuthGlue(0, 3 * wordSpaceIPD.opt, 0,
+                        (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
                                        new LeafPosition(this, -1), true));
                     iNextStart ++;
                     break;
@@ -715,8 +773,7 @@ public class TextLayoutManager extends AbstractLayoutManager {
                 iNextStart ++;
             } else if (textArray[iNextStart] == NEWLINE) {
                 // linefeed; this can happen when linefeed-treatment="preserve"
-                // the linefeed character is the first one in textArray,
-                // so we can just return a list with a penalty item
+                // add a penalty item to the list and return
                 returnList.add
                     (new KnuthPenalty(0, -KnuthElement.INFINITE,
                                       false, null, false));
@@ -747,14 +804,15 @@ public class TextLayoutManager extends AbstractLayoutManager {
                     // constant letter space; simply return a box
                     // whose width includes letter spaces
                     returnList.add
-                        (new KnuthBox(wordIPD.opt, 0, 0, 0,
+                        (new KnuthBox(wordIPD.opt, lead, total, middle,
                                       new LeafPosition(this, vecAreaInfo.size() - 1), false));
                     iNextStart = iTempStart;
                 } else {
                     // adjustable letter space;
                     // some other KnuthElements are needed
                     returnList.add
-                        (new KnuthBox(wordIPD.opt - (iTempStart - iThisStart - 1) * letterSpaceIPD.opt, 0, 0, 0,
+                        (new KnuthBox(wordIPD.opt - (iTempStart - iThisStart - 1) * letterSpaceIPD.opt,
+                                      lead, total, middle,
                                       new LeafPosition(this, vecAreaInfo.size() - 1), false));
                     returnList.add
                         (new KnuthPenalty(0, KnuthElement.INFINITE, false,
@@ -765,7 +823,7 @@ public class TextLayoutManager extends AbstractLayoutManager {
                                        (iTempStart - iThisStart - 1) * (letterSpaceIPD.opt - letterSpaceIPD.min),
                                        new LeafPosition(this, -1), true));
                     returnList.add
-                        (new KnuthBox(0, 0, 0, 0,
+                        (new KnuthBox(0, lead, total, middle,
                                       new LeafPosition(this, -1), true));
                     iNextStart = iTempStart;
                 }
@@ -779,17 +837,13 @@ public class TextLayoutManager extends AbstractLayoutManager {
         }
     }
 
-    public int getWordSpaceIPD() {
-        return wordSpaceIPD.opt;
-    }
-
     public KnuthElement addALetterSpaceTo(KnuthElement element) {
         LeafPosition pos = (LeafPosition) element.getPosition();
         AreaInfo ai = (AreaInfo) vecAreaInfo.get(pos.getLeafPos());
         ai.iLScount ++;
         ai.ipdArea.add(letterSpaceIPD);
         if (letterSpaceIPD.min == letterSpaceIPD.max) {
-            return new KnuthBox(ai.ipdArea.opt, 0, 0, 0, pos, false);
+            return new KnuthBox(ai.ipdArea.opt, lead, total, middle, pos, false);
         } else {
             return new KnuthGlue(ai.iLScount * letterSpaceIPD.opt,
                                  ai.iLScount * (letterSpaceIPD.max - letterSpaceIPD.opt),
@@ -908,13 +962,13 @@ public class TextLayoutManager extends AbstractLayoutManager {
                 // ai refers either to a word or a word fragment
                 if (letterSpaceIPD.min == letterSpaceIPD.max) {
                     returnList.add
-                        (new KnuthBox(ai.ipdArea.opt, 0, 0, 0,
+                        (new KnuthBox(ai.ipdArea.opt, lead, total, middle,
                                       new LeafPosition(this, iReturnedIndex), false));
                 } else {
                     returnList.add
                         (new KnuthBox(ai.ipdArea.opt
                                       - ai.iLScount * letterSpaceIPD.opt,
-                                      0, 0, 0
+                                      lead, total, middle
                                       new LeafPosition(this, iReturnedIndex), false));
                     returnList.add
                         (new KnuthPenalty(0, KnuthElement.INFINITE, false,
@@ -939,14 +993,14 @@ public class TextLayoutManager extends AbstractLayoutManager {
                 switch (alignment) {
                 case CENTER :
                     returnList.add
-                        (new KnuthGlue(0, 3 * wordSpaceIPD.opt, 0,
+                        (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
                                        new LeafPosition(this, iReturnedIndex), false));
                     returnList.add
                         (new KnuthPenalty(0, 0, false,
                                           new LeafPosition(this, -1), true));
                     returnList.add
                         (new KnuthGlue(wordSpaceIPD.opt,
-                                       - 6 * wordSpaceIPD.opt, 0,
+                                       - 6 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
                                        new LeafPosition(this, -1), true));
                     returnList.add
                         (new KnuthBox(0, 0, 0, 0,
@@ -955,7 +1009,7 @@ public class TextLayoutManager extends AbstractLayoutManager {
                         (new KnuthPenalty(0, KnuthElement.INFINITE, false,
                                           new LeafPosition(this, -1), true));
                     returnList.add
-                        (new KnuthGlue(0, 3 * wordSpaceIPD.opt, 0,
+                        (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
                                        new LeafPosition(this, -1), true));
                     iReturnedIndex ++;
                     break;