diff options
Diffstat (limited to 'src/java/org/apache/fop/layoutmgr')
10 files changed, 756 insertions, 417 deletions
diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java index d091c73e5..cd011b366 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java @@ -187,6 +187,7 @@ public abstract class AbstractBreaker { alignment = Constants.EN_START; } alignmentLast = Constants.EN_START; + childLC.setBPAlignment(alignment); BlockSequence blockList; blockLists = new java.util.ArrayList(); diff --git a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java index 636055cb5..45e2945fa 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java @@ -166,7 +166,10 @@ public class BlockLayoutManager extends BlockStackingLayoutManager { */ public boolean mustKeepTogether() { //TODO Keeps will have to be more sophisticated sooner or later - return ((BlockLevelLayoutManager)getParent()).mustKeepTogether() + // TODO This is a quick fix for the fact that the parent is not always a BlockLevelLM; + // eventually mustKeepTogether() must be moved up to the LM interface + return (getParent() instanceof BlockLevelLayoutManager + && ((BlockLevelLayoutManager) getParent()).mustKeepTogether()) || !getBlockFO().getKeepTogether().getWithinPage().isAuto() || !getBlockFO().getKeepTogether().getWithinColumn().isAuto(); } @@ -303,7 +306,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager { splitLength += element.getW(); lastLM = element.getLayoutManager(); } - } + } //System.out.println("addAreas riferito a storedList da " + // iFirst + " a " + iLast); //System.out.println("splitLength= " + splitLength diff --git a/src/java/org/apache/fop/layoutmgr/KnuthSequence.java b/src/java/org/apache/fop/layoutmgr/KnuthSequence.java index e757cb002..77f30f1c5 100644 --- a/src/java/org/apache/fop/layoutmgr/KnuthSequence.java +++ b/src/java/org/apache/fop/layoutmgr/KnuthSequence.java @@ -28,6 +28,8 @@ public class KnuthSequence extends ArrayList { public int ignoreAtStart = 0; /** Number of elements to ignore at the end of the list. */ public int ignoreAtEnd = 0; + // Is this an inline or a block sequence? + private boolean isInlineSequence = false; /** * Creates a new and empty list. @@ -37,6 +39,14 @@ public class KnuthSequence extends ArrayList { } /** + * Creates a new and empty list, and sets isInlineSequence. + */ + public KnuthSequence(boolean isInlineSequence) { + super(); + this.isInlineSequence = isInlineSequence; + } + + /** * Marks the start of the sequence. */ public void startSequence() { @@ -84,4 +94,12 @@ public class KnuthSequence extends ArrayList { public KnuthElement getElement(int index) { return (KnuthElement) get(index); } + + /** + * Is this an inline or a block sequence? + * @return true if this is an inline sequence + */ + public boolean isInlineSequence() { + return isInlineSequence; + } } diff --git a/src/java/org/apache/fop/layoutmgr/LayoutContext.java b/src/java/org/apache/fop/layoutmgr/LayoutContext.java index 2d3323625..f63f23ccf 100644 --- a/src/java/org/apache/fop/layoutmgr/LayoutContext.java +++ b/src/java/org/apache/fop/layoutmgr/LayoutContext.java @@ -91,6 +91,9 @@ public class LayoutContext { /** Current hyphenation context. May be null. */ private HyphContext hyphContext = null; + /** Alignment in BP direction */ + private int bpAlignment = Constants.EN_START; + /** Stretch or shrink value when making areas. */ private double ipdAdjust = 0.0; @@ -112,6 +115,7 @@ public class LayoutContext { this.leadingSpace = parentLC.leadingSpace; //??? this.trailingSpace = parentLC.trailingSpace; //??? this.hyphContext = parentLC.hyphContext; + this.bpAlignment = parentLC.bpAlignment; this.dSpaceAdjust = parentLC.dSpaceAdjust; this.ipdAdjust = parentLC.ipdAdjust; this.iLineHeight = parentLC.iLineHeight; @@ -224,6 +228,19 @@ public class LayoutContext { return ((this.flags & TRY_HYPHENATE) != 0); } + /** + * Sets the currently applicable alignment in BP direction. + * @param alignment one of EN_START, EN_JUSTIFY etc. + */ + public void setBPAlignment(int alignment) { + this.bpAlignment = alignment; + } + + /** @return the currently applicable alignment in BP direction (EN_START, EN_JUSTIFY...) */ + public int getBPAlignment() { + return this.bpAlignment; + } + public void setSpaceAdjust(double adjust) { dSpaceAdjust = adjust; } diff --git a/src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java index 9dd0abbcb..c6d5c4bf7 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/BasicLinkLayoutManager.java @@ -20,6 +20,7 @@ package org.apache.fop.layoutmgr.inline; import org.apache.fop.fo.flow.BasicLink; import org.apache.fop.layoutmgr.LayoutManager; +import org.apache.fop.area.inline.InlineArea; import org.apache.fop.area.inline.InlineParent; import org.apache.fop.area.Trait; import org.apache.fop.area.LinkResolver; @@ -41,14 +42,14 @@ public class BasicLinkLayoutManager extends InlineLayoutManager { fobj = node; } - protected InlineParent createArea() { - InlineParent area = super.createArea(); + protected InlineArea createArea(boolean bInlineParent) { + InlineArea area = super.createArea(bInlineParent); setupBasicLinkArea(parentLM, area); return area; } private void setupBasicLinkArea(LayoutManager parentLM, - InlineParent area) { + InlineArea area) { if (fobj.getExternalDestination() != null) { area.addTrait(Trait.EXTERNAL_LINK, fobj.getExternalDestination()); } else { diff --git a/src/java/org/apache/fop/layoutmgr/inline/FootnoteLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/FootnoteLayoutManager.java index e39292f62..779c4c587 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/FootnoteLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/FootnoteLayoutManager.java @@ -26,6 +26,7 @@ import org.apache.fop.fo.flow.Footnote; import org.apache.fop.layoutmgr.AbstractLayoutManager; import org.apache.fop.layoutmgr.FootnoteBodyLayoutManager; import org.apache.fop.layoutmgr.KnuthElement; +import org.apache.fop.layoutmgr.KnuthSequence; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.Position; @@ -45,7 +46,7 @@ public class FootnoteLayoutManager extends AbstractLayoutManager footnote = node; // create an InlineStackingLM handling the fo:inline child of fo:footnote - citationLM = new InlineStackingLayoutManager(footnote.getFootnoteCitation()); + citationLM = new InlineLayoutManager(footnote.getFootnoteCitation()); // create a FootnoteBodyLM handling the fo:footnote-body child of fo:footnote bodyLM = new FootnoteBodyLayoutManager(footnote.getFootnoteBody()); @@ -85,12 +86,24 @@ public class FootnoteLayoutManager extends AbstractLayoutManager private void addAnchor(LinkedList citationList) { // find the last box in the sequence, and add a reference // to the FootnoteBodyLM - ListIterator citationIterator = citationList.listIterator(citationList.size()); KnuthInlineBox lastBox = null; + ListIterator citationIterator = citationList.listIterator(citationList.size()); while (citationIterator.hasPrevious() && lastBox == null) { - KnuthElement element = (KnuthElement) citationIterator.previous(); - if (element instanceof KnuthInlineBox) { - lastBox = (KnuthInlineBox) element; + Object obj = citationIterator.previous(); + if (obj instanceof KnuthElement) { + KnuthElement element = (KnuthElement)obj; + if (element instanceof KnuthInlineBox) { + lastBox = (KnuthInlineBox) element; + } + } else { + KnuthSequence seq = (KnuthSequence)obj; + ListIterator nestedIterator = seq.listIterator(seq.size()); + while (nestedIterator.hasPrevious() && lastBox == null) { + KnuthElement element = (KnuthElement)nestedIterator.previous(); + if (element instanceof KnuthInlineBox) { + lastBox = (KnuthInlineBox) element; + } + } } } if (lastBox != null) { diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java index 7f0943a9f..72b8c7bad 100755 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java @@ -21,6 +21,9 @@ package org.apache.fop.layoutmgr.inline; import java.util.ListIterator; import java.util.LinkedList; +import org.apache.fop.area.Area; +import org.apache.fop.area.inline.InlineArea; +import org.apache.fop.area.inline.InlineBlockParent; import org.apache.fop.area.inline.InlineParent; import org.apache.fop.fo.flow.Inline; import org.apache.fop.fo.flow.InlineLevel; @@ -28,10 +31,14 @@ import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonMarginInline; import org.apache.fop.fo.properties.SpaceProperty; import org.apache.fop.layoutmgr.KnuthElement; +import org.apache.fop.layoutmgr.KnuthSequence; +import org.apache.fop.layoutmgr.KnuthPenalty; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.NonLeafPosition; import org.apache.fop.layoutmgr.SpaceSpecifier; import org.apache.fop.layoutmgr.TraitSetter; +import org.apache.fop.layoutmgr.LayoutManager; +import org.apache.fop.layoutmgr.PositionIterator; import org.apache.fop.traits.MinOptMax; import org.apache.fop.traits.SpaceVal; @@ -46,6 +53,9 @@ public class InlineLayoutManager extends InlineStackingLayoutManager private CommonMarginInline inlineProps = null; private CommonBorderPaddingBackground borderProps = null; + private boolean bAreaCreated = false; + private LayoutManager lastChildLM = null; // Set when return last breakposs; + /** * Create an inline layout manager. * This is used for fo's that create areas that @@ -109,8 +119,14 @@ public class InlineLayoutManager extends InlineStackingLayoutManager } /** @see org.apache.fop.layoutmgr.inline.InlineStackingLayoutManager#createArea() */ - protected InlineParent createArea() { - InlineParent area = super.createArea(); + protected InlineArea createArea(boolean bInlineParent) { + InlineArea area; + if (bInlineParent) { + area = new InlineParent(); + area.setOffset(0); + } else { + area = new InlineBlockParent(); + } TraitSetter.setProducerID(area, getInlineFO().getId()); return area; } @@ -132,7 +148,8 @@ public class InlineLayoutManager extends InlineStackingLayoutManager /** @see org.apache.fop.layoutmgr.LayoutManager */ public LinkedList getNextKnuthElements(LayoutContext lc, int alignment) { - InlineLevelLayoutManager curLM; + InlineLevelLayoutManager curILM; + LayoutManager curLM, lastLM = null; // the list returned by child LM LinkedList returnedList; @@ -140,6 +157,7 @@ public class InlineLayoutManager extends InlineStackingLayoutManager // the list which will be returned to the parent LM LinkedList returnList = new LinkedList(); + KnuthSequence lastSequence = null; SpaceSpecifier leadingSpace = lc.getLeadingSpace(); @@ -160,27 +178,236 @@ public class InlineLayoutManager extends InlineStackingLayoutManager clearPrevIPD(); // Clear stored prev content dimensions } - while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) { + StringBuffer trace = new StringBuffer("InlineLM:"); + + while ((curLM = (LayoutManager) getChildLM()) != null) { // get KnuthElements from curLM returnedList = curLM.getNextKnuthElements(lc, alignment); - if (returnedList != null) { + if (returnedList == null) { + // curLM returned null because it finished; + // just iterate once more to see if there is another child + continue; + } + if (curLM instanceof InlineLevelLayoutManager) { + // close the last block sequence + if (lastSequence != null && !lastSequence.isInlineSequence()) { + lastSequence = null; + if (log.isTraceEnabled()) { + trace.append(" ]"); + } + } // "wrap" the Position stored in each element of returnedList - ListIterator listIter = returnedList.listIterator(); - while (listIter.hasNext()) { - returnedElement = (KnuthElement) listIter.next(); - returnedElement.setPosition + ListIterator seqIter = returnedList.listIterator(); + while (seqIter.hasNext()) { + KnuthSequence sequence = (KnuthSequence) seqIter.next(); + ListIterator listIter = sequence.listIterator(); + while (listIter.hasNext()) { + returnedElement = (KnuthElement) listIter.next(); + returnedElement.setPosition (new NonLeafPosition(this, - returnedElement.getPosition())); - returnList.add(returnedElement); + returnedElement.getPosition())); + } + if (!sequence.isInlineSequence()) { + if (lastSequence != null && lastSequence.isInlineSequence()) { + // log.error("Last inline sequence should be closed before a block sequence"); + lastSequence.add(new KnuthPenalty(0, -KnuthElement.INFINITE, + false, null, false)); + lastSequence = null; + if (log.isTraceEnabled()) { + trace.append(" ]"); + } + } + returnList.add(sequence); + if (log.isTraceEnabled()) { + trace.append(" B"); + } + } else { + if (lastSequence == null) { + lastSequence = new KnuthSequence(true); + returnList.add(lastSequence); + if (log.isTraceEnabled()) { + trace.append(" ["); + } + } else { + if (log.isTraceEnabled()) { + trace.append(" +"); + } + } + lastSequence.addAll(sequence); + if (log.isTraceEnabled()) { + trace.append(" I"); + } + // finish last paragraph if it was closed with a linefeed + KnuthElement lastElement = (KnuthElement) sequence.getLast(); + if (lastElement.isPenalty() + && ((KnuthPenalty) lastElement).getP() + == -KnuthPenalty.INFINITE) { + // a penalty item whose value is -inf + // represents a preserved linefeed, + // wich forces a line break + lastSequence = null; + if (log.isTraceEnabled()) { + trace.append(" ]"); + } + } + } + } + } else { // A block LM + // close the last inline sequence + if (lastSequence != null && lastSequence.isInlineSequence()) { + lastSequence.add(new KnuthPenalty(0, -KnuthElement.INFINITE, + false, null, false)); + lastSequence = null; + if (log.isTraceEnabled()) { + trace.append(" ]"); + } + } + if (curLM != lastLM) { + // close the last block sequence + if (lastSequence != null && !lastSequence.isInlineSequence()) { + lastSequence = null; + if (log.isTraceEnabled()) { + trace.append(" ]"); + } + } + lastLM = curLM; + } + if (lastSequence == null) { + lastSequence = new KnuthSequence(false); + returnList.add(lastSequence); + if (log.isTraceEnabled()) { + trace.append(" ["); + } + } else { + if (log.isTraceEnabled()) { + trace.append(" +"); + } + } + ListIterator iter = returnedList.listIterator(); + while (iter.hasNext()) { + KnuthElement element = (KnuthElement) iter.next(); + element.setPosition + (new NonLeafPosition(this, + element.getPosition())); + } + lastSequence.addAll(returnedList); + if (log.isTraceEnabled()) { + trace.append(" L"); } - return returnList; - } else { - // curLM returned null because it finished; - // just iterate once more to see if there is another child } } setFinished(true); - return null; + log.trace(trace); + return returnList.size() == 0 ? null : returnList; + } + + /** + * Generate and add areas to parent area. + * Set size of each area. This should only create and return one + * inline area for any inline parent area. + * + * @param parentIter Iterator over Position information returned + * by this LayoutManager. + * @param dSpaceAdjust Factor controlling how much extra space to add + * in order to justify the line. + */ + public void addAreas(PositionIterator parentIter, + LayoutContext context) { + addId(); + + setChildContext(new LayoutContext(context)); // Store current value + + // If has fence, make a new leadingSS + /* How to know if first area created by this LM? Keep a count and + * reset it if getNextBreakPoss() is called again. + */ + if (hasLeadingFence(bAreaCreated)) { + getContext().setLeadingSpace(new SpaceSpecifier(false)); + getContext().setFlags(LayoutContext.RESOLVE_LEADING_SPACE, + true); + } else { + getContext().setFlags(LayoutContext.RESOLVE_LEADING_SPACE, + false); + } + + if (getSpaceStart() != null) { + context.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart())); + } + + // "unwrap" the NonLeafPositions stored in parentIter + // and put them in a new list; + // also set lastLM to be the LayoutManager which created + // the last Position: if the LAST_AREA flag is set in context, + // it must be also set in the LayoutContext given to lastLM, + // but unset in the LayoutContext given to the other LMs + LinkedList positionList = new LinkedList(); + NonLeafPosition pos = null; + LayoutManager lastLM = null; // last child LM in this iterator + while (parentIter.hasNext()) { + pos = (NonLeafPosition) parentIter.next(); + positionList.add(pos.getPosition()); + } + if (pos != null) { + lastLM = pos.getPosition().getLM(); + } + + InlineArea parent = createArea(lastLM == null || lastLM instanceof InlineLevelLayoutManager); + parent.setBPD(context.getLineHeight()); + setCurrentArea(parent); + + StackingIter childPosIter + = new StackingIter(positionList.listIterator()); + + LayoutManager prevLM = null; + LayoutManager childLM; + while ((childLM = childPosIter.getNextChildLM()) + != null) { + getContext().setFlags(LayoutContext.LAST_AREA, + context.isLastArea() && childLM == lastLM); + childLM.addAreas(childPosIter, getContext()); + getContext().setLeadingSpace(getContext().getTrailingSpace()); + getContext().setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true); + prevLM = childLM; + } + + /* If has trailing fence, + * resolve trailing space specs from descendants. + * Otherwise, propagate any trailing space specs to parent LM via + * the context object. + * If the last child LM called return ISLAST in the context object + * and it is the last child LM for this LM, then this must be + * the last area for the current LM also. + */ + boolean bIsLast = + (getContext().isLastArea() && prevLM == lastChildLM); + if (hasTrailingFence(bIsLast)) { + addSpace(getCurrentArea(), + getContext().getTrailingSpace().resolve(false), + getContext().getSpaceAdjust()); + context.setTrailingSpace(new SpaceSpecifier(false)); + } else { + // Propagate trailing space-spec sequence to parent LM in context + context.setTrailingSpace(getContext().getTrailingSpace()); + } + // Add own trailing space to parent context (or set on area?) + if (context.getTrailingSpace() != null && getSpaceEnd() != null) { + context.getTrailingSpace().addSpace(new SpaceVal(getSpaceEnd())); + } + setTraits(bAreaCreated, !bIsLast); + + parentLM.addChildArea(getCurrentArea()); + context.setFlags(LayoutContext.LAST_AREA, bIsLast); + bAreaCreated = true; + } + + public void addChildArea(Area childArea) { + Area parent = getCurrentArea(); + if (getContext().resolveLeadingSpace()) { + addSpace(parent, + getContext().getLeadingSpace().resolve(false), + getContext().getSpaceAdjust()); + } + parent.addChildArea(childArea); } /* diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java index 0b05cc8aa..eb8eab717 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java @@ -33,11 +33,7 @@ import org.apache.fop.layoutmgr.LayoutManager; import org.apache.fop.layoutmgr.NonLeafPosition; import org.apache.fop.layoutmgr.Position; import org.apache.fop.layoutmgr.PositionIterator; -import org.apache.fop.layoutmgr.SpaceSpecifier; -import org.apache.fop.traits.SpaceVal; import org.apache.fop.area.Area; -import org.apache.fop.area.inline.InlineArea; -import org.apache.fop.area.inline.InlineParent; import org.apache.fop.area.inline.Space; import org.apache.fop.traits.MinOptMax; @@ -50,7 +46,7 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager implements InlineLevelLayoutManager { - private static class StackingIter extends PositionIterator { + protected static class StackingIter extends PositionIterator { StackingIter(Iterator parentIter) { super(parentIter); @@ -81,7 +77,6 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager //private BreakPoss prevBP; protected LayoutContext childLC; - private LayoutManager lastChildLM = null; // Set when return last breakposs private boolean bAreaCreated = false; //private LayoutManager currentLM = null; @@ -177,10 +172,6 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager hmPrevIPD.clear(); } - protected InlineParent createArea() { - return new InlineParent(); - } - /** * This method is called by addAreas() so IDs can be added to a page for FOs that * support the 'id' property. @@ -189,103 +180,6 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager // Do nothing here, overriden in subclasses that have an 'id' property. } - /** - * Generate and add areas to parent area. - * Set size of each area. This should only create and return one - * inline area for any inline parent area. - * - * @param parentIter Iterator over Position information returned - * by this LayoutManager. - * @param dSpaceAdjust Factor controlling how much extra space to add - * in order to justify the line. - */ - public void addAreas(PositionIterator parentIter, - LayoutContext context) { - addId(); - InlineParent parent = createArea(); - parent.setBPD(context.getLineHeight()); - parent.setOffset(0); - setCurrentArea(parent); - - setChildContext(new LayoutContext(context)); // Store current value - - // If has fence, make a new leadingSS - /* How to know if first area created by this LM? Keep a count and - * reset it if getNextBreakPoss() is called again. - */ - if (hasLeadingFence(bAreaCreated)) { - getContext().setLeadingSpace(new SpaceSpecifier(false)); - getContext().setFlags(LayoutContext.RESOLVE_LEADING_SPACE, - true); - } else { - getContext().setFlags(LayoutContext.RESOLVE_LEADING_SPACE, - false); - } - - if (getSpaceStart() != null) { - context.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart())); - } - - // "unwrap" the NonLeafPositions stored in parentIter - // and put them in a new list; - // also set lastLM to be the LayoutManager which created - // the last Position: if the LAST_AREA flag is set in context, - // it must be also set in the LayoutContext given to lastLM, - // but unset in the LayoutContext given to the other LMs - LinkedList positionList = new LinkedList(); - NonLeafPosition pos; - LayoutManager lastLM = null; // last child LM in this iterator - while (parentIter.hasNext()) { - pos = (NonLeafPosition) parentIter.next(); - lastLM = pos.getPosition().getLM(); - positionList.add(pos.getPosition()); - } - - StackingIter childPosIter - = new StackingIter(positionList.listIterator()); - - LayoutManager prevLM = null; - InlineLevelLayoutManager childLM; - while ((childLM = (InlineLevelLayoutManager) childPosIter.getNextChildLM()) - != null) { - getContext().setFlags(LayoutContext.LAST_AREA, - context.isLastArea() && childLM == lastLM); - childLM.addAreas(childPosIter, getContext()); - getContext().setLeadingSpace(getContext().getTrailingSpace()); - getContext().setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true); - prevLM = childLM; - } - - /* If has trailing fence, - * resolve trailing space specs from descendants. - * Otherwise, propagate any trailing space specs to parent LM via - * the context object. - * If the last child LM called return ISLAST in the context object - * and it is the last child LM for this LM, then this must be - * the last area for the current LM also. - */ - boolean bIsLast = - (getContext().isLastArea() && prevLM == lastChildLM); - if (hasTrailingFence(bIsLast)) { - addSpace(getCurrentArea(), - getContext().getTrailingSpace().resolve(false), - getContext().getSpaceAdjust()); - context.setTrailingSpace(new SpaceSpecifier(false)); - } else { - // Propagate trailing space-spec sequence to parent LM in context - context.setTrailingSpace(getContext().getTrailingSpace()); - } - // Add own trailing space to parent context (or set on area?) - if (context.getTrailingSpace() != null && getSpaceEnd() != null) { - context.getTrailingSpace().addSpace(new SpaceVal(getSpaceEnd())); - } - setTraits(bAreaCreated, !bIsLast); - - parentLM.addChildArea(getCurrentArea()); - context.setFlags(LayoutContext.LAST_AREA, bIsLast); - bAreaCreated = true; - } - protected Area getCurrentArea() { return currentArea; } @@ -298,19 +192,6 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager } - public void addChildArea(Area childArea) { - // Make sure childArea is inline area - if (childArea instanceof InlineArea) { - Area parent = getCurrentArea(); - if (getContext().resolveLeadingSpace()) { - addSpace(parent, - getContext().getLeadingSpace().resolve(false), - getContext().getSpaceAdjust()); - } - parent.addChildArea(childArea); - } - } - protected void setChildContext(LayoutContext lc) { childLC = lc; } @@ -342,57 +223,6 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager } } - 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); - 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); - } - setFinished(curLM.isFinished() && (getChildLM() == null)); - 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 List addALetterSpaceTo(List oldList) { // old list contains only a box, or the sequence: box penalty glue box @@ -407,7 +237,7 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager oldList = ((InlineLevelLayoutManager) element.getLayoutManager()).addALetterSpaceTo(oldList); - // "wrap" againg the Position stored in each element of oldList + // "wrap" again the Position stored in each element of oldList oldListIterator = oldList.listIterator(); while (oldListIterator.hasNext()) { element = (KnuthElement) oldListIterator.next(); diff --git a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java index c3091d66c..f51e6af2b 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java @@ -37,10 +37,13 @@ import org.apache.fop.layoutmgr.KnuthSequence; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.LayoutManager; import org.apache.fop.layoutmgr.LeafPosition; +import org.apache.fop.layoutmgr.NonLeafPosition; import org.apache.fop.layoutmgr.Position; import org.apache.fop.layoutmgr.PositionIterator; import org.apache.fop.layoutmgr.SpaceSpecifier; +import org.apache.fop.area.Area; import org.apache.fop.area.LineArea; +import org.apache.fop.area.inline.InlineArea; import java.util.ListIterator; import java.util.List; @@ -184,7 +187,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager public Paragraph(LineLayoutManager llm, int alignment, int alignmentLast, int indent) { - super(); + super(true); layoutManager = llm; textAlignment = alignment; textAlignmentLast = alignmentLast; @@ -268,6 +271,40 @@ public class LineLayoutManager extends InlineStackingLayoutManager } } + private void addALetterSpace() { + KnuthBox prevBox = (KnuthBox) removeLast(); + LinkedList oldList = new LinkedList(); + // 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; + oldList.add(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 + oldList.add(prevBox); + oldList.addFirst((KnuthGlue) removeLast()); + oldList.addFirst((KnuthPenalty) removeLast()); + oldList.addFirst((KnuthBox) removeLast()); + } + // adding a letter space could involve, according to the text + // represented by oldList, replacing a glue element or adding + // new elements + addAll(((InlineLevelLayoutManager) + prevBox.getLayoutManager()) + .addALetterSpaceTo(oldList)); + if (((KnuthInlineBox) prevBox).isAnchor()) { + // prevBox represents a footnote citation: copy footnote info + // from prevBox to the new box + KnuthInlineBox newBox = (KnuthInlineBox) getLast(); + newBox.setFootnoteBodyLM(((KnuthInlineBox) prevBox).getFootnoteBodyLM()); + } + } } private class LineBreakingAlgorithm extends BreakingAlgorithm { @@ -510,6 +547,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager initialize(); // Normally done when started by parent! } + /** @see org.apache.fop.layoutmgr.LayoutManager */ public LinkedList getNextKnuthElements(LayoutContext context, int alignment) { // Get a break from currently active child LM // Set up constraints for inline level managers @@ -546,7 +584,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager } //PHASE 2: Create line breaks - return findOptimalLineBreakingPoints(alignment); + return createLineBreaks(context.getBPAlignment()); /* LineBreakPosition lbp = null; if (breakpoints == null) { @@ -586,19 +624,17 @@ public class LineLayoutManager extends InlineStackingLayoutManager LayoutContext inlineLC = new LayoutContext(context); InlineLevelLayoutManager curLM; - KnuthElement thisElement = null; LinkedList returnedList = null; iLineWidth = context.getStackLimit().opt; // convert all the text in a sequence of paragraphs made // of KnuthBox, KnuthGlue and KnuthPenalty objects boolean bPrevWasKnuthBox = false; - KnuthBox prevBox = null; - Paragraph knuthPar = new Paragraph(this, - bTextAlignment, bTextAlignmentLast, - textIndent.getValue()); - knuthPar.startParagraph(availIPD.opt); + StringBuffer trace = new StringBuffer("LineLM:"); + + Paragraph lastPar = null; + while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) { if ((returnedList = curLM.getNextKnuthElements(inlineLC, @@ -607,78 +643,119 @@ public class LineLayoutManager extends InlineStackingLayoutManager if (returnedList.size() == 0) { continue; } - // look at the first element - thisElement = (KnuthElement) returnedList.getFirst(); - if (thisElement.isBox() && !thisElement.isAuxiliary() - && bPrevWasKnuthBox) { - prevBox = (KnuthBox) knuthPar.removeLast(); - LinkedList oldList = new LinkedList(); - // 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; - oldList.add(prevBox); + // does the first element of the first paragraph add to an existing word? + if (lastPar != null) { + Object obj = returnedList.getFirst(); + KnuthElement thisElement; + if (obj instanceof KnuthElement) { + thisElement = (KnuthElement)obj; } 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 - oldList.add(prevBox); - oldList.addFirst((KnuthGlue) knuthPar.removeLast()); - oldList.addFirst((KnuthPenalty) knuthPar.removeLast()); - oldList.addFirst((KnuthBox) knuthPar.removeLast()); + KnuthSequence firstSeq = (KnuthSequence) obj; + if (!firstSeq.isInlineSequence()) { + log.error("Expect inline sequence as first sequence when last paragraph is not null"); + } + thisElement = (KnuthElement) firstSeq.get(0); } - // adding a letter space could involve, according to the text - // represented by oldList, replacing a glue element or adding - // new elements - knuthPar.addAll(((InlineLevelLayoutManager) - prevBox.getLayoutManager()) - .addALetterSpaceTo(oldList)); - if (((KnuthInlineBox) prevBox).isAnchor()) { - // prevBox represents a footnote citation: copy footnote info - // from prevBox to the new box - KnuthInlineBox newBox = (KnuthInlineBox) knuthPar.getLast(); - newBox.setFootnoteBodyLM(((KnuthInlineBox) prevBox).getFootnoteBodyLM()); + if (thisElement.isBox() && !thisElement.isAuxiliary() + && bPrevWasKnuthBox) { + lastPar.addALetterSpace(); } } - // look at the last element - KnuthElement lastElement = (KnuthElement) returnedList.getLast(); - boolean bForceLinefeed = false; - if (lastElement.isBox()) { - bPrevWasKnuthBox = true; - } else { - 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(); + // loop over the KnuthSequences (and single KnuthElements) in returnedList + // (LeafNodeLM descendants may also skip wrapping elements in KnuthSequences + // to cause fewer container structures) + // TODO the mixture here adds a little to the complexity. Decide whether: + // - to leave as is and save some container instances + // - to use KnuthSequences exclusively (adjustments on leaf-type LMs necessary) + // See also FootnoteLM.addAnchor() as well as right above this comment + // for similar code. Or see http://svn.apache.org/viewcvs?rev=230779&view=rev + ListIterator iter = returnedList.listIterator(); + while (iter.hasNext()) { + Object obj = iter.next(); + KnuthElement singleElement = null; + KnuthSequence sequence = null; + if (obj instanceof KnuthElement) { + singleElement = (KnuthElement)obj; + } else { + sequence = (KnuthSequence)obj; } - } + // the sequence contains inline Knuth elements + if (singleElement != null || sequence.isInlineSequence()) { + // look at the last element + KnuthElement lastElement; + if (singleElement != null) { + lastElement = singleElement; + } else { + lastElement = (KnuthElement) sequence.getLast(); + if (lastElement == null) { + throw new NullPointerException( + "Sequence was empty! lastElement is null"); + } + } + bPrevWasKnuthBox = lastElement.isBox(); + + // if last paragraph is open, add the new elements to the paragraph + // else this is the last paragraph + if (lastPar == null) { + lastPar = new Paragraph(this, + bTextAlignment, bTextAlignmentLast, + textIndent.getValue()); + lastPar.startParagraph(availIPD.opt); + if (log.isTraceEnabled()) { + trace.append(" ["); + } + } else { + if (log.isTraceEnabled()) { + trace.append(" +"); + } + } + if (singleElement != null) { + lastPar.add(singleElement); + } else { + lastPar.addAll(sequence); + } + if (log.isTraceEnabled()) { + trace.append(" I"); + } - // add the new elements to the paragraph - knuthPar.addAll(returnedList); - if (bForceLinefeed) { - if (knuthPar.size() == 0) { - //only a forced linefeed on this line - //-> compensate with a zero width box - knuthPar.add(new KnuthInlineBox(0, 0, 0, 0, - null, false)); + // finish last paragraph if it was closed with a linefeed + if (lastElement.isPenalty() + && ((KnuthPenalty) lastElement).getP() + == -KnuthPenalty.INFINITE) { + // a penalty item whose value is -inf + // represents a preserved linefeed, + // wich forces a line break + lastPar.removeLast(); + if (lastPar.size() == 0) { + //only a forced linefeed on this line + //-> compensate with a zero width box + lastPar.add(new KnuthInlineBox(0, 0, 0, 0, + null, false)); + } + lastPar.endParagraph(); + ElementListObserver.observe(lastPar, "line", null); + lastPar = null; + if (log.isTraceEnabled()) { + trace.append(" ]"); + } + bPrevWasKnuthBox = false; + } + } else { // the sequence is a block sequence +/* // "wrap" the Position stored in each element of returnedList + ListIterator listIter = sequence.listIterator(); + while (listIter.hasNext()) { + KnuthElement returnedElement = (KnuthElement) listIter.next(); + returnedElement.setPosition + (new NonLeafPosition(this, + returnedElement.getPosition())); + } +*/ knuthParagraphs.add(sequence); + if (log.isTraceEnabled()) { + trace.append(" B"); + } } - knuthPar.endParagraph(); - knuthPar = new Paragraph(this, - bTextAlignment, bTextAlignmentLast, - textIndent.getValue()); - knuthPar.startParagraph(availIPD.opt); - bPrevWasKnuthBox = false; - } + } // end of loop over returnedList } else { // curLM returned null; this can happen // if it has nothing more to layout, @@ -686,8 +763,14 @@ public class LineLayoutManager extends InlineStackingLayoutManager // if there are other children } } - knuthPar.endParagraph(); - ElementListObserver.observe(knuthPar, "line", null); + if (lastPar != null) { + lastPar.endParagraph(); + ElementListObserver.observe(lastPar, "line", null); + if (log.isTraceEnabled()) { + trace.append(" ]"); + } + } + log.trace(trace); } /** @@ -822,121 +905,167 @@ public class LineLayoutManager extends InlineStackingLayoutManager /** * Phase 2 of Knuth algorithm: find optimal break points. - * @param alignment alignmenr of the paragraph + * @param alignment alignment in BP direction of the paragraph * @return a list of Knuth elements representing broken lines */ - private LinkedList findOptimalLineBreakingPoints(int alignment) { + private LinkedList createLineBreaks(int alignment) { // find the optimal line breaking points for each paragraph ListIterator paragraphsIterator = knuthParagraphs.listIterator(knuthParagraphs.size()); - Paragraph currPar = null; - LineBreakingAlgorithm alg; lineLayoutsList = new ArrayList(knuthParagraphs.size()); while (paragraphsIterator.hasPrevious()) { - lineLayouts = new LineLayoutPossibilities(); - currPar = (Paragraph) paragraphsIterator.previous(); - double maxAdjustment = 1; - int iBPcount = 0; - alg = new LineBreakingAlgorithm(alignment, - bTextAlignment, bTextAlignmentLast, - textIndent.getValue(), currPar.lineFiller.opt, - lineHeight, lead, follow, middleShift, - (knuthParagraphs.indexOf(currPar) == 0), - this); - - if (hyphProps.hyphenate == EN_TRUE) { - findHyphenationPoints(currPar); - } - - // first try - boolean bHyphenationAllowed = false; - alg.setConstantLineWidth(iLineWidth); - iBPcount = alg.findBreakingPoints(currPar, - maxAdjustment, false, bHyphenationAllowed); - if (iBPcount == 0 || alignment == EN_JUSTIFY) { - // if the first try found a set of breaking points, save them - if (iBPcount > 0) { - alg.resetAlgorithm(); - lineLayouts.savePossibilities(false); - } else { - // the first try failed - log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment); - } - - // now try something different - log.debug("Hyphenation possible? " + (hyphProps.hyphenate == EN_TRUE)); - if (hyphProps.hyphenate == EN_TRUE) { - // consider every hyphenation point as a legal break - bHyphenationAllowed = true; - } else { - // try with a higher threshold - maxAdjustment = 5; - } - - if ((iBPcount - = alg.findBreakingPoints(currPar, - maxAdjustment, false, bHyphenationAllowed)) == 0) { - // the second try failed too, try with a huge threshold - // and force the algorithm to find - // a set of breaking points - log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment - + (hyphProps.hyphenate == EN_TRUE ? " and hyphenation" : "")); - maxAdjustment = 20; - iBPcount - = alg.findBreakingPoints(currPar, - maxAdjustment, true, bHyphenationAllowed); - } - - // use non-hyphenated breaks, when possible - lineLayouts.restorePossibilities(); - - /* extension (not in the XSL FO recommendation): if vertical alignment - is justify and the paragraph has only one layout, try using - shorter or longer lines */ - //TODO This code snippet is disabled. Reenable? - if (false && alignment == EN_JUSTIFY && bTextAlignment == EN_JUSTIFY) { - //System.out.println("LLM.getNextKnuthElements> layouts with more lines? " + lineLayouts.canUseMoreLines()); - //System.out.println(" layouts with fewer lines? " + lineLayouts.canUseLessLines()); - if (!lineLayouts.canUseMoreLines()) { - alg.resetAlgorithm(); - lineLayouts.savePossibilities(true); - // try with shorter lines - int savedLineWidth = iLineWidth; - iLineWidth = (int) (iLineWidth * 0.95); - iBPcount = alg.findBreakingPoints(currPar, - maxAdjustment, true, bHyphenationAllowed); - // use normal lines, when possible - lineLayouts.restorePossibilities(); - iLineWidth = savedLineWidth; - } - if (!lineLayouts.canUseLessLines()) { - alg.resetAlgorithm(); - lineLayouts.savePossibilities(true); - // try with longer lines - int savedLineWidth = iLineWidth; - iLineWidth = (int) (iLineWidth * 1.05); - alg.setConstantLineWidth(iLineWidth); - iBPcount = alg.findBreakingPoints(currPar, - maxAdjustment, true, bHyphenationAllowed); - // use normal lines, when possible - lineLayouts.restorePossibilities(); - iLineWidth = savedLineWidth; - } - //System.out.println("LLM.getNextKnuthElements> now, layouts with more lines? " + lineLayouts.canUseMoreLines()); - //System.out.println(" now, layouts with fewer lines? " + lineLayouts.canUseLessLines()); - } + KnuthSequence seq = (KnuthSequence) paragraphsIterator.previous(); + if (!seq.isInlineSequence()) { + lineLayouts = createBlockLineBreak(seq); + } else { + lineLayouts = findOptimalBreakingPoints(alignment, (Paragraph) seq); } lineLayoutsList.add(0, lineLayouts); } - setFinished(true); //Post-process the line breaks found return postProcessLineBreaks(alignment); } + /** + * create a single line layout possibility with a single linebreak + * for a block sequence + * @param seq the Knuth sequence for which the linebreak is created + * @return the line layout possibilities for the paragraph + */ + private LineLayoutPossibilities createBlockLineBreak(KnuthSequence seq) { + //TODO Should this really create only a single LineBreakPosition??? + //This creates an implicit keep-together on the nested block-level FOs. + lineLayouts = new LineLayoutPossibilities(); + lineLayouts.addPossibility(1,0); + int lineHeight = 0, lineStretch = 0, lineShrink = 0; + ListIterator seqIterator = seq.listIterator(); + while (seqIterator.hasNext()) { + KnuthElement element = (KnuthElement) seqIterator.next(); + lineHeight += element.getW(); + if (element.isGlue()) { + lineStretch += element.getY(); + lineShrink += element.getZ(); + } + } + LineBreakPosition lbp = new LineBreakPosition(this, + knuthParagraphs.indexOf(seq), seq.size() - 1, + lineShrink, lineStretch, 0, 0, 0, 0, lineHeight, + iLineWidth, 0, 0, 0); + lineLayouts.addBreakPosition(lbp, 0); + return lineLayouts; + } + + /** + * Fint the optimal linebreaks for a paragraph + * @param alignment alignment of the paragraph + * @param currPar the Paragraph for which the linebreaks are found + * @return the line layout possibilities for the paragraph + */ + private LineLayoutPossibilities findOptimalBreakingPoints(int alignment, Paragraph currPar) { + lineLayouts = new LineLayoutPossibilities(); + double maxAdjustment = 1; + int iBPcount = 0; + LineBreakingAlgorithm alg = new LineBreakingAlgorithm(alignment, + bTextAlignment, bTextAlignmentLast, + textIndent.getValue(), currPar.lineFiller.opt, + lineHeight, lead, follow, middleShift, + (knuthParagraphs.indexOf(currPar) == 0), + this); + + if (hyphProps.hyphenate == EN_TRUE) { + findHyphenationPoints(currPar); + } + + // first try + boolean bHyphenationAllowed = false; + alg.setConstantLineWidth(iLineWidth); + iBPcount = alg.findBreakingPoints(currPar, + maxAdjustment, false, bHyphenationAllowed); + if (iBPcount == 0 || alignment == EN_JUSTIFY) { + // if the first try found a set of breaking points, save them + if (iBPcount > 0) { + alg.resetAlgorithm(); + lineLayouts.savePossibilities(false); + } else { + // the first try failed + log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment); + } + + // now try something different + log.debug("Hyphenation possible? " + (hyphProps.hyphenate == EN_TRUE)); + if (hyphProps.hyphenate == EN_TRUE) { + // consider every hyphenation point as a legal break + bHyphenationAllowed = true; + } else { + // try with a higher threshold + maxAdjustment = 5; + } + + if ((iBPcount + = alg.findBreakingPoints(currPar, + maxAdjustment, false, bHyphenationAllowed)) == 0) { + // the second try failed too, try with a huge threshold + // and force the algorithm to find + // a set of breaking points + log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment + + (hyphProps.hyphenate == EN_TRUE ? " and hyphenation" : "")); + maxAdjustment = 20; + iBPcount + = alg.findBreakingPoints(currPar, + maxAdjustment, true, bHyphenationAllowed); + } + + // use non-hyphenated breaks, when possible + lineLayouts.restorePossibilities(); + + /* extension (not in the XSL FO recommendation): if vertical alignment + is justify and the paragraph has only one layout, try using + shorter or longer lines */ + //TODO This code snippet is disabled. Reenable? + if (false && alignment == EN_JUSTIFY && bTextAlignment == EN_JUSTIFY) { + //System.out.println("LLM.getNextKnuthElements> layouts with more lines? " + lineLayouts.canUseMoreLines()); + //System.out.println(" layouts with fewer lines? " + lineLayouts.canUseLessLines()); + if (!lineLayouts.canUseMoreLines()) { + alg.resetAlgorithm(); + lineLayouts.savePossibilities(true); + // try with shorter lines + int savedLineWidth = iLineWidth; + iLineWidth = (int) (iLineWidth * 0.95); + iBPcount = alg.findBreakingPoints(currPar, + maxAdjustment, true, bHyphenationAllowed); + // use normal lines, when possible + lineLayouts.restorePossibilities(); + iLineWidth = savedLineWidth; + } + if (!lineLayouts.canUseLessLines()) { + alg.resetAlgorithm(); + lineLayouts.savePossibilities(true); + // try with longer lines + int savedLineWidth = iLineWidth; + iLineWidth = (int) (iLineWidth * 1.05); + alg.setConstantLineWidth(iLineWidth); + iBPcount = alg.findBreakingPoints(currPar, + maxAdjustment, true, bHyphenationAllowed); + // use normal lines, when possible + lineLayouts.restorePossibilities(); + iLineWidth = savedLineWidth; + } + //System.out.println("LLM.getNextKnuthElements> now, layouts with more lines? " + lineLayouts.canUseMoreLines()); + //System.out.println(" now, layouts with fewer lines? " + lineLayouts.canUseLessLines()); + } + } + return lineLayouts; + } + + /** + * Creates the element list in BP direction for the broken lines. + * @param alignment the currently applicable vertical alignment + * @return the newly built element list + */ private LinkedList postProcessLineBreaks(int alignment) { LinkedList returnList = new LinkedList(); @@ -949,8 +1078,22 @@ public class LineLayoutManager extends InlineStackingLayoutManager } lineLayouts = (LineLayoutPossibilities)lineLayoutsList.get(p); - - if (alignment == EN_JUSTIFY) { + KnuthSequence seq = (KnuthSequence) knuthParagraphs.get(p); + + if (!seq.isInlineSequence()) { + LinkedList targetList = new LinkedList(); + ListIterator listIter = seq.listIterator(); + while (listIter.hasNext()) { + KnuthElement tempElement; + tempElement = (KnuthElement) listIter.next(); + if (tempElement.getLayoutManager() != this) { + tempElement.setPosition(new NonLeafPosition(this, + tempElement.getPosition())); + } + targetList.add(tempElement); + } + returnList.addAll(targetList); + } else if (seq.isInlineSequence() && alignment == EN_JUSTIFY) { /* justified vertical alignment (not in the XSL FO recommendation): create a multi-layout sequence whose elements will contain a conventional Position */ @@ -975,17 +1118,28 @@ public class LineLayoutManager extends InlineStackingLayoutManager // create a list of the FootnoteBodyLM handling footnotes // whose citations are in this line LinkedList footnoteList = new LinkedList(); - ListIterator elementIterator = ((Paragraph) knuthParagraphs.get(p)).listIterator(startIndex); + ListIterator elementIterator = seq.listIterator(startIndex); while (elementIterator.nextIndex() <= endIndex) { KnuthElement element = (KnuthElement) elementIterator.next(); if (element instanceof KnuthInlineBox && ((KnuthInlineBox) element).isAnchor()) { footnoteList.add(((KnuthInlineBox) element).getFootnoteBodyLM()); + } else if (element instanceof KnuthBlockBox) { + footnoteList.addAll(((KnuthBlockBox) element).getFootnoteBodyLMs()); } } startIndex = endIndex + 1; - returnList.add(new KnuthBlockBox(((LineBreakPosition) lineLayouts.getChosenPosition(i)).lineHeight, - footnoteList, lineLayouts.getChosenPosition(i), false)); + LineBreakPosition lbp = (LineBreakPosition) lineLayouts.getChosenPosition(i); + returnList.add(new KnuthBlockBox(lbp.lineHeight, footnoteList, lbp, false)); + /* // add stretch and shrink to the returnlist + if (!seq.isInlineSequence() + && lbp.availableStretch != 0 || lbp.availableShrink != 0) { + returnList.add(new KnuthPenalty(0, -KnuthElement.INFINITE, + false, new Position(this), false)); + returnList.add(new KnuthGlue(0, lbp.availableStretch, lbp.availableShrink, + new Position(this), false)); + } + */ } } } @@ -1403,7 +1557,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager while (parentIter.hasNext()) { Position pos = (Position) parentIter.next(); if (pos instanceof LineBreakPosition) { - ListIterator paragraphIterator = null; + ListIterator seqIterator = null; KnuthElement tempElement = null; // the TLM which created the last KnuthElement in this line LayoutManager lastLM = null; @@ -1412,6 +1566,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager LineArea lineArea = new LineArea(); lineArea.setStartIndent(lbp.startIndent); lineArea.setBPD(lbp.lineHeight); + lineArea.setIPD(lbp.lineWidth); lc.setBaseline(lbp.baseline); lc.setLineHeight(lbp.lineHeight); lc.setMiddleShift(middleShift); @@ -1419,43 +1574,46 @@ public class LineLayoutManager extends InlineStackingLayoutManager lc.setBottomShift(lbp.bottomShift); iCurrParIndex = lbp.iParIndex; - Paragraph currPar = (Paragraph) knuthParagraphs.get(iCurrParIndex); + KnuthSequence seq = (KnuthSequence) knuthParagraphs.get(iCurrParIndex); iEndElement = lbp.getLeafPos(); - // ignore the first elements added by the LineLayoutManager - iStartElement += (iStartElement == 0) ? currPar.ignoreAtStart : 0; - - // ignore the last elements added by the LineLayoutManager - iEndElement -= (iEndElement == (currPar.size() - 1)) + if (seq instanceof Paragraph) { + Paragraph currPar = (Paragraph) seq; + // ignore the first elements added by the LineLayoutManager + iStartElement += (iStartElement == 0) ? currPar.ignoreAtStart : 0; + + // ignore the last elements added by the LineLayoutManager + iEndElement -= (iEndElement == (currPar.size() - 1)) ? currPar.ignoreAtEnd : 0; - + } + // ignore the last element in the line if it is a KnuthGlue object - paragraphIterator = currPar.listIterator(iEndElement); - tempElement = (KnuthElement) paragraphIterator.next(); + seqIterator = seq.listIterator(iEndElement); + tempElement = (KnuthElement) seqIterator.next(); if (tempElement.isGlue()) { iEndElement --; // this returns the same KnuthElement - paragraphIterator.previous(); - tempElement = (KnuthElement) paragraphIterator.previous(); + seqIterator.previous(); + tempElement = (KnuthElement) seqIterator.previous(); } lastLM = tempElement.getLayoutManager(); // ignore KnuthGlue and KnuthPenalty objects // at the beginning of the line - paragraphIterator = currPar.listIterator(iStartElement); - tempElement = (KnuthElement) paragraphIterator.next(); - while (!tempElement.isBox() && paragraphIterator.hasNext()) { - tempElement = (KnuthElement) paragraphIterator.next(); + seqIterator = seq.listIterator(iStartElement); + tempElement = (KnuthElement) seqIterator.next(); + while (!tempElement.isBox() && seqIterator.hasNext()) { + tempElement = (KnuthElement) seqIterator.next(); iStartElement ++; } // Add the inline areas to lineArea PositionIterator inlinePosIter - = new KnuthPossPosIter(currPar, iStartElement, + = new KnuthPossPosIter(seq, iStartElement, iEndElement + 1); iStartElement = lbp.getLeafPos() + 1; - if (iStartElement == currPar.size()) { + if (iStartElement == seq.size()) { // advance to next paragraph iStartElement = 0; } @@ -1507,11 +1665,73 @@ public class LineLayoutManager extends InlineStackingLayoutManager lineArea.setBPD(lineArea.getBPD() + context.getSpaceAfter()); } parentLM.addChildArea(lineArea); + } else if (pos instanceof NonLeafPosition) { + // Nested block-level content; + // go down the LM stack again; + // collect all consecutive NonLeafPosition objects, + // "unwrap" them and put the child positions in a new list. + LinkedList positionList = new LinkedList(); + Position innerPosition; + innerPosition = ((NonLeafPosition) pos).getPosition(); + positionList.add(innerPosition); + while (parentIter.hasNext()) { + pos = (Position)parentIter.peekNext(); + if (!(pos instanceof NonLeafPosition)) { + break; + } + pos = (Position) parentIter.next(); + innerPosition = ((NonLeafPosition) pos).getPosition(); + positionList.add(innerPosition); + } + + // do we have the last LM? + LayoutManager lastLM = null; + if (!parentIter.hasNext()) { + lastLM = innerPosition.getLM(); + } + + // this may be wrong; not all areas belong inside a single line area + // see InlineStackingLM.addChildArea + LineArea lineArea = new LineArea(); + setCurrentArea(lineArea); + setChildContext(lc); + + PositionIterator childPosIter = new StackingIter(positionList.listIterator()); + LayoutContext blocklc = new LayoutContext(0); + blocklc.setLeadingSpace(new SpaceSpecifier(true)); + blocklc.setTrailingSpace(new SpaceSpecifier(false)); + blocklc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true); + while ((childLM = childPosIter.getNextChildLM()) != null) { + // set last area flag + blocklc.setFlags(LayoutContext.LAST_AREA, + (context.isLastArea() && childLM == lastLM)); + blocklc.setStackLimit(context.getStackLimit()); + // Add the line areas to Area + childLM.addAreas(childPosIter, blocklc); + blocklc.setLeadingSpace(blocklc.getTrailingSpace()); + blocklc.setTrailingSpace(new SpaceSpecifier(false)); + } + lineArea.updateExtentsFromChildren(); + parentLM.addChildArea(lineArea); } else { // pos was the Position inside a penalty item, nothing to do } } setCurrentArea(null); // ?? necessary } + + public void addChildArea(Area childArea) { + // Make sure childArea is inline area + if (childArea instanceof InlineArea) { + Area parent = getCurrentArea(); + if (getContext().resolveLeadingSpace()) { + addSpace(parent, + getContext().getLeadingSpace().resolve(false), + getContext().getSpaceAdjust()); + } + parent.addChildArea(childArea); + } + } + } diff --git a/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java index 1a33d196c..77d560df1 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java @@ -26,6 +26,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.layoutmgr.KnuthSequence; import org.apache.fop.layoutmgr.KnuthBox; import org.apache.fop.layoutmgr.KnuthElement; import org.apache.fop.layoutmgr.KnuthGlue; @@ -418,6 +419,8 @@ public class TextLayoutManager extends LeafNodeLayoutManager { public LinkedList getNextKnuthElements(LayoutContext context, int alignment) { LinkedList returnList = new LinkedList(); + KnuthSequence sequence = new KnuthSequence(true); + returnList.add(sequence); while (iNextStart < textArray.length) { if (textArray[iNextStart] == SPACE @@ -425,7 +428,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { // normal, breaking space // or non-breaking space if (textArray[iNextStart] == NBSPACE) { - returnList.add + sequence.add (new KnuthPenalty(0, KnuthElement.INFINITE, false, new LeafPosition(this, vecAreaInfo.size() - 1), false)); @@ -436,23 +439,23 @@ public class TextLayoutManager extends LeafNodeLayoutManager { (new AreaInfo(iNextStart, (short) (iNextStart + 1), (short) 1, (short) 0, wordSpaceIPD, false)); - returnList.add + sequence.add (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, new LeafPosition(this, vecAreaInfo.size() - 1), false)); - returnList.add + sequence.add (new KnuthPenalty(0, 0, false, new LeafPosition(this, -1), true)); - returnList.add + sequence.add (new KnuthGlue(wordSpaceIPD.opt, - 6 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, new LeafPosition(this, -1), true)); - returnList.add + sequence.add (new KnuthInlineBox(0, 0, 0, 0, new LeafPosition(this, -1), true)); - returnList.add + sequence.add (new KnuthPenalty(0, KnuthElement.INFINITE, false, new LeafPosition(this, -1), true)); - returnList.add + sequence.add (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, new LeafPosition(this, -1), true)); iNextStart ++; @@ -464,13 +467,13 @@ public class TextLayoutManager extends LeafNodeLayoutManager { (new AreaInfo(iNextStart, (short) (iNextStart + 1), (short) 1, (short) 0, wordSpaceIPD, false)); - returnList.add + sequence.add (new KnuthGlue(0, 3 * wordSpaceIPD.opt, 0, new LeafPosition(this, vecAreaInfo.size() - 1), false)); - returnList.add + sequence.add (new KnuthPenalty(0, 0, false, new LeafPosition(this, -1), true)); - returnList.add + sequence.add (new KnuthGlue(wordSpaceIPD.opt, - 3 * wordSpaceIPD.opt, 0, new LeafPosition(this, -1), true)); @@ -482,7 +485,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { (new AreaInfo(iNextStart, (short) (iNextStart + 1), (short) 1, (short) 0, wordSpaceIPD, false)); - returnList.add + sequence.add (new KnuthGlue(wordSpaceIPD.opt, wordSpaceIPD.max - wordSpaceIPD.opt, wordSpaceIPD.opt - wordSpaceIPD.min, @@ -495,7 +498,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { (new AreaInfo(iNextStart, (short) (iNextStart + 1), (short) 1, (short) 0, wordSpaceIPD, false)); - returnList.add + sequence.add (new KnuthGlue(wordSpaceIPD.opt, wordSpaceIPD.max - wordSpaceIPD.opt, 0, new LeafPosition(this, vecAreaInfo.size() - 1), false)); @@ -507,10 +510,10 @@ public class TextLayoutManager extends LeafNodeLayoutManager { (new AreaInfo(iNextStart, (short) (iNextStart + 1), (short) 1, (short) 0, wordSpaceIPD, false)); - returnList.add + sequence.add (new KnuthPenalty(0, KnuthElement.INFINITE, false, new LeafPosition(this, vecAreaInfo.size() - 1), false)); - returnList.add + sequence.add (new KnuthGlue(wordSpaceIPD.opt, wordSpaceIPD.max - wordSpaceIPD.opt, wordSpaceIPD.opt - wordSpaceIPD.min, @@ -518,12 +521,13 @@ public class TextLayoutManager extends LeafNodeLayoutManager { iNextStart ++; } else if (textArray[iNextStart] == NEWLINE) { // linefeed; this can happen when linefeed-treatment="preserve" - // add a penalty item to the list and return - returnList.add + // add a penalty item to the list and start a new sequence + sequence.add (new KnuthPenalty(0, -KnuthElement.INFINITE, false, null, false)); + sequence = new KnuthSequence(true); + returnList.add(sequence); iNextStart ++; - return returnList; } else { // the beginning of a word iThisStart = iNextStart; @@ -548,25 +552,25 @@ public class TextLayoutManager extends LeafNodeLayoutManager { if (letterSpaceIPD.min == letterSpaceIPD.max) { // constant letter space; simply return a box // whose width includes letter spaces - returnList.add + sequence.add (new KnuthInlineBox(wordIPD.opt, lead, total, middle, new LeafPosition(this, vecAreaInfo.size() - 1), false)); } else { // adjustable letter space; // some other KnuthElements are needed - returnList.add + sequence.add (new KnuthInlineBox(wordIPD.opt - iLetterSpaces * letterSpaceIPD.opt, lead, total, middle, new LeafPosition(this, vecAreaInfo.size() - 1), false)); - returnList.add + sequence.add (new KnuthPenalty(0, KnuthElement.INFINITE, false, new LeafPosition(this, -1), true)); - returnList.add + sequence.add (new KnuthGlue(iLetterSpaces * letterSpaceIPD.opt, iLetterSpaces * (letterSpaceIPD.max - letterSpaceIPD.opt), iLetterSpaces * (letterSpaceIPD.opt - letterSpaceIPD.min), new LeafPosition(this, -1), true)); - returnList.add + sequence.add (new KnuthInlineBox(0, lead, total, middle, new LeafPosition(this, -1), true)); } @@ -577,10 +581,10 @@ public class TextLayoutManager extends LeafNodeLayoutManager { && iTempStart < textArray.length && textArray[iTempStart] != SPACE && textArray[iTempStart] != NBSPACE) { - returnList.add + sequence.add (new KnuthPenalty(0, KnuthPenalty.FLAGGED_PENALTY, true, new LeafPosition(this, -1), false)); - returnList.add + sequence.add (new KnuthGlue(letterSpaceIPD.opt, letterSpaceIPD.max - letterSpaceIPD.opt, letterSpaceIPD.opt - letterSpaceIPD.min, @@ -592,6 +596,10 @@ public class TextLayoutManager extends LeafNodeLayoutManager { iNextStart = iTempStart; } } // end of while + if (((List)returnList.getLast()).size() == 0) { + //Remove an empty sequence because of a trailing newline + returnList.removeLast(); + } setFinished(true); if (returnList.size() > 0) { return returnList; @@ -605,7 +613,8 @@ public class TextLayoutManager extends LeafNodeLayoutManager { // look at the Position stored in the first element in oldList // which is always a box ListIterator oldListIterator = oldList.listIterator(); - LeafPosition pos = (LeafPosition) ((KnuthBox) oldListIterator.next()).getPosition(); + KnuthElement el = (KnuthElement)oldListIterator.next(); + LeafPosition pos = (LeafPosition) ((KnuthBox) el).getPosition(); AreaInfo ai = (AreaInfo) vecAreaInfo.get(pos.getLeafPos()); ai.iLScount ++; ai.ipdArea.add(letterSpaceIPD); |