Index: area/inline/FilledArea.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/java/org/apache/fop/area/inline/FilledArea.java,v retrieving revision 1.3 diff -u -r1.3 FilledArea.java --- area/inline/FilledArea.java 22 Sep 2004 08:18:42 -0000 1.3 +++ area/inline/FilledArea.java 12 Nov 2004 14:17:46 -0000 @@ -19,6 +19,7 @@ package org.apache.fop.area.inline; import java.util.List; +import java.util.ListIterator; import java.util.ArrayList; /** @@ -37,6 +38,29 @@ * Create a new filled area. */ 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); + } + } } /** Index: fo/flow/Character.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/java/org/apache/fop/fo/flow/Character.java,v retrieving revision 1.29 diff -u -r1.29 Character.java --- fo/flow/Character.java 29 Oct 2004 11:19:36 -0000 1.29 +++ fo/flow/Character.java 12 Nov 2004 14:18:04 -0000 @@ -84,6 +84,7 @@ 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 @@ 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); } @@ -222,6 +224,13 @@ */ public Property getWordSpacing() { return wordSpacing; + } + + /** + * Return the "vertical-align" property. + */ + public int getVerticalAlign() { + return verticalAlign; } /** Index: fo/flow/Inline.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/java/org/apache/fop/fo/flow/Inline.java,v retrieving revision 1.34 diff -u -r1.34 Inline.java --- fo/flow/Inline.java 29 Oct 2004 11:19:36 -0000 1.34 +++ fo/flow/Inline.java 12 Nov 2004 14:18:05 -0000 @@ -66,6 +66,7 @@ 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 @@ 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(); @@ -212,6 +214,13 @@ */ public int getTextDecoration() { return textDecoration; + } + + /** + * Return the "vertical-align" property. + */ + public int getVerticalAlign() { + return verticalAlign; } /** Index: fo/flow/Leader.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/java/org/apache/fop/fo/flow/Leader.java,v retrieving revision 1.47 diff -u -r1.47 Leader.java --- fo/flow/Leader.java 29 Oct 2004 11:19:36 -0000 1.47 +++ fo/flow/Leader.java 12 Nov 2004 14:18:06 -0000 @@ -54,6 +54,7 @@ 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 @@ 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 @@ 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. */ @@ -191,6 +207,13 @@ */ public Length getLeaderPatternWidth() { return leaderPatternWidth; + } + + /** + * Return the "vertical-align" property. + */ + public int getVerticalAlign() { + return verticalAlign; } /** Index: layoutmgr/AbstractLayoutManager.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java,v retrieving revision 1.30 diff -u -r1.30 AbstractLayoutManager.java --- layoutmgr/AbstractLayoutManager.java 24 Oct 2004 00:03:50 -0000 1.30 +++ layoutmgr/AbstractLayoutManager.java 12 Nov 2004 14:18:47 -0000 @@ -275,43 +275,6 @@ * 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: layoutmgr/BlockLayoutManager.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java,v retrieving revision 1.32 diff -u -r1.32 BlockLayoutManager.java --- layoutmgr/BlockLayoutManager.java 20 Oct 2004 11:55:32 -0000 1.32 +++ layoutmgr/BlockLayoutManager.java 12 Nov 2004 14:18:49 -0000 @@ -57,6 +57,7 @@ private int lead = 12000; private int lineHeight = 14000; private int follow = 2000; + private int middleShift = 0; private int iStartPos = 0; @@ -71,6 +72,7 @@ lead = fs.getAscender(); follow = -fs.getDescender(); + middleShift = -fs.getXHeight() / 2; lineHeight = fobj.getLineHeight().getOptimum().getLength().getValue(); } @@ -141,7 +143,7 @@ */ 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: layoutmgr/CharacterLayoutManager.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/java/org/apache/fop/layoutmgr/CharacterLayoutManager.java,v retrieving revision 1.7 diff -u -r1.7 CharacterLayoutManager.java --- layoutmgr/CharacterLayoutManager.java 20 Oct 2004 13:19:24 -0000 1.7 +++ layoutmgr/CharacterLayoutManager.java 12 Nov 2004 14:18:50 -0000 @@ -48,6 +48,7 @@ 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 @@ */ 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 @@ 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: layoutmgr/ContentLayoutManager.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/java/org/apache/fop/layoutmgr/ContentLayoutManager.java,v retrieving revision 1.15 diff -u -r1.15 ContentLayoutManager.java --- layoutmgr/ContentLayoutManager.java 24 Oct 2004 00:03:50 -0000 1.15 +++ layoutmgr/ContentLayoutManager.java 12 Nov 2004 14:18:52 -0000 @@ -22,6 +22,7 @@ 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 @@ * 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 @@ 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; } @@ -202,9 +216,6 @@ } /** @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 @@ * @see org.apache.fop.layoutmgr.LayoutManager#getChildLMs */ public List getChildLMs() { + List childLMs = new ArrayList(1); + childLMs.add(childLM); return childLMs; } @@ -271,7 +284,7 @@ } 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 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) { @@ -314,10 +345,6 @@ int flaggedPenalty, int alignment) { return null; - } - - public int getWordSpaceIPD() { - return 0; } } Index: layoutmgr/InlineLayoutManager.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/java/org/apache/fop/layoutmgr/InlineLayoutManager.java,v retrieving revision 1.2 diff -u -r1.2 InlineLayoutManager.java --- layoutmgr/InlineLayoutManager.java 11 Nov 2004 06:51:58 -0000 1.2 +++ layoutmgr/InlineLayoutManager.java 12 Nov 2004 14:18:54 -0000 @@ -18,8 +18,13 @@ 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 @@ * 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 @@ 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 @@ 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 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; + } } Index: layoutmgr/InlineStackingLayoutManager.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/java/org/apache/fop/layoutmgr/InlineStackingLayoutManager.java,v retrieving revision 1.16 diff -u -r1.16 InlineStackingLayoutManager.java --- layoutmgr/InlineStackingLayoutManager.java 11 Nov 2004 06:38:11 -0000 1.16 +++ layoutmgr/InlineStackingLayoutManager.java 12 Nov 2004 14:18:56 -0000 @@ -69,7 +69,7 @@ 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 @@ = 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()); @@ -555,212 +556,6 @@ ls.setIPD(iAdjust); parentArea.addChild(ls); } - } - } - - 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: layoutmgr/LayoutContext.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/java/org/apache/fop/layoutmgr/LayoutContext.java,v retrieving revision 1.6 diff -u -r1.6 LayoutContext.java --- layoutmgr/LayoutContext.java 5 Sep 2004 18:16:32 -0000 1.6 +++ layoutmgr/LayoutContext.java 12 Nov 2004 14:18:59 -0000 @@ -86,6 +86,7 @@ private int iLineHeight; private int iBaseline; + private int iMiddleShift; public LayoutContext(LayoutContext parentLC) { this.flags = parentLC.flags; @@ -98,6 +99,7 @@ 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 @@ 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 @@ "\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: layoutmgr/LayoutManager.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/java/org/apache/fop/layoutmgr/LayoutManager.java,v retrieving revision 1.14 diff -u -r1.14 LayoutManager.java --- layoutmgr/LayoutManager.java 24 Oct 2004 00:03:50 -0000 1.14 +++ layoutmgr/LayoutManager.java 12 Nov 2004 14:19:00 -0000 @@ -237,19 +237,4 @@ * @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: layoutmgr/LeaderLayoutManager.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/java/org/apache/fop/layoutmgr/LeaderLayoutManager.java,v retrieving revision 1.11 diff -u -r1.11 LeaderLayoutManager.java --- layoutmgr/LeaderLayoutManager.java 20 Oct 2004 21:07:02 -0000 1.11 +++ layoutmgr/LeaderLayoutManager.java 12 Nov 2004 14:19:02 -0000 @@ -23,7 +23,10 @@ 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 @@ private Leader fobj; Font font = null; + private LinkedList contentList = null; + private ContentLayoutManager clm = null; + /** * Constructor * @@ -48,7 +54,10 @@ 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 @@ 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 @@ // 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 @@ 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 @@ 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: layoutmgr/LeafNodeLayoutManager.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/java/org/apache/fop/layoutmgr/LeafNodeLayoutManager.java,v retrieving revision 1.10 diff -u -r1.10 LeafNodeLayoutManager.java --- layoutmgr/LeafNodeLayoutManager.java 20 Oct 2004 13:41:06 -0000 1.10 +++ layoutmgr/LeafNodeLayoutManager.java 12 Nov 2004 14:19:03 -0000 @@ -33,12 +33,13 @@ * 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 @@ * @param al the vertical alignment positioning */ public void setAlignment(int al) { - alignment = al; + verticalAlignment = al; } /** @@ -147,49 +148,6 @@ } /** - * 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. * @param refIPD the ipd of the parent reference area @@ -200,19 +158,6 @@ } /** - * 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. * @param posIter the position iterator @@ -244,9 +189,9 @@ */ 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 @@ 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 @@ 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 void hyphenate(Position pos, HyphContext hc) { - // use the AbstractLayoutManager.hyphenate() null implementation - super.hyphenate(pos, hc); } public boolean applyChanges(List oldList) { Index: layoutmgr/LineLayoutManager.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/java/org/apache/fop/layoutmgr/LineLayoutManager.java,v retrieving revision 1.32 diff -u -r1.32 LineLayoutManager.java --- layoutmgr/LineLayoutManager.java 11 Nov 2004 08:11:07 -0000 1.32 +++ layoutmgr/LineLayoutManager.java 12 Nov 2004 14:19:13 -0000 @@ -58,7 +58,7 @@ * @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 @@ lineHeight = lh; lead = l; follow = f; + middleShift = ms; initialize(); // Normally done when started by parent! } @@ -126,6 +127,8 @@ 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 @@ // 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 @@ // 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 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 @@ 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 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 @@ 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 - // - // the letter space is added to , - // 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 + // + // the letter space is added to , + // 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 @@ // 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 @@ 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 @@ } 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ // 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 @@ lineArea.setBPD(lbp.lineHeight); lc.setBaseline(lbp.baseline); lc.setLineHeight(lbp.lineHeight); + lc.setMiddleShift(middleShift); setCurrentArea(lineArea); Paragraph currPar = (Paragraph) knuthParagraphs.get(iCurrParIndex); Index: layoutmgr/RetrieveMarkerLayoutManager.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/java/org/apache/fop/layoutmgr/RetrieveMarkerLayoutManager.java,v retrieving revision 1.14 diff -u -r1.14 RetrieveMarkerLayoutManager.java --- layoutmgr/RetrieveMarkerLayoutManager.java 28 Oct 2004 10:00:25 -0000 1.14 +++ layoutmgr/RetrieveMarkerLayoutManager.java 12 Nov 2004 14:19:15 -0000 @@ -73,7 +73,8 @@ if (replaceLM == null) { return null; } - return replaceLM.getNextKnuthElements(context, alignment); + return ((InlineLevelLayoutManager) replaceLM) + .getNextKnuthElements(context, alignment); } public void addAreas(PositionIterator parentIter, Index: layoutmgr/TextLayoutManager.java =================================================================== RCS file: /home/cvspublic/xml-fop/src/java/org/apache/fop/layoutmgr/TextLayoutManager.java,v retrieving revision 1.25 diff -u -r1.25 TextLayoutManager.java --- layoutmgr/TextLayoutManager.java 20 Oct 2004 13:41:06 -0000 1.25 +++ layoutmgr/TextLayoutManager.java 12 Nov 2004 14:19:22 -0000 @@ -24,6 +24,7 @@ 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 @@ * 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 @@ 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 @@ // 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 @@ * 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 @@ 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 @@ * @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 @@ 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 @@ (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 @@ (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 @@ 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 @@ // 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 @@ (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 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 @@ // 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 @@ 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 @@ (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;