From 8a75941b7921f77e3656f47114b07f5adc591a3a Mon Sep 17 00:00:00 2001 From: Simon Pepping Date: Thu, 4 Aug 2005 20:29:50 +0000 Subject: [PATCH] My work on block content in inline content sofar. Block content in fo:inline is implemented. Basic rendering of this content is in place. BasicLinkLM uses the new structure for returning new Knuth elements git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/inlineblock@227471 13f79535-47bb-0310-9956-ffa450edef68 --- .../fop/area/inline/InlineBlockParent.java | 64 ++ .../fop/layoutmgr/BlockLayoutManager.java | 7 +- .../apache/fop/layoutmgr/KnuthSequence.java | 18 + .../inline/BasicLinkLayoutManager.java | 7 +- .../layoutmgr/inline/InlineLayoutManager.java | 259 +++++++- .../inline/InlineStackingLayoutManager.java | 170 +----- .../layoutmgr/inline/LineLayoutManager.java | 558 +++++++++++------- .../layoutmgr/inline/TextLayoutManager.java | 54 +- .../apache/fop/render/AbstractRenderer.java | 33 +- .../apache/fop/render/xml/XMLRenderer.java | 26 +- test/layoutengine/disabled-testcases.txt | 2 + test/layoutengine/testcases/inline-block1.xml | 57 ++ test/layoutengine/testcases/inline-block2.xml | 75 +++ 13 files changed, 896 insertions(+), 434 deletions(-) create mode 100644 src/java/org/apache/fop/area/inline/InlineBlockParent.java create mode 100644 test/layoutengine/testcases/inline-block1.xml create mode 100644 test/layoutengine/testcases/inline-block2.xml diff --git a/src/java/org/apache/fop/area/inline/InlineBlockParent.java b/src/java/org/apache/fop/area/inline/InlineBlockParent.java new file mode 100644 index 000000000..65126c809 --- /dev/null +++ b/src/java/org/apache/fop/area/inline/InlineBlockParent.java @@ -0,0 +1,64 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.area.inline; + +import org.apache.fop.area.Area; +import org.apache.fop.area.Block; + + +/** + * Inline block parent area. + * This is an inline area that can have one block area as a child + */ +public class InlineBlockParent extends InlineArea { + + /** + * The list of inline areas added to this inline parent. + */ + protected Block child = null; + + /** + * Create a new inline block parent to add areas to. + */ + public InlineBlockParent() { + } + + /** + * Override generic Area method. + * + * @param childArea the child area to add + */ + public void addChildArea(Area childArea) { + if (childArea instanceof Block && child == null) { + child = (Block) childArea; + } else { + // TODO: log error, raise exception? + } + } + + /** + * Get the child areas for this inline parent. + * + * @return the list of child areas + */ + public Block getChildArea() { + return child; + } + +} 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. @@ -36,6 +38,14 @@ public class KnuthSequence extends ArrayList { super(); } + /** + * Creates a new and empty list, and sets isInlineSequence. + */ + public KnuthSequence(boolean isInlineSequence) { + super(); + this.isInlineSequence = isInlineSequence; + } + /** * Marks the start of the sequence. */ @@ -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/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/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..df4671fd0 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java @@ -34,9 +34,7 @@ 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 +48,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 +79,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 +174,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 +182,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 +194,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 +225,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 diff --git a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java index c3091d66c..df647fcaf 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java @@ -40,7 +40,9 @@ import org.apache.fop.layoutmgr.LeafPosition; 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 +186,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 +270,41 @@ 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 + // + // the letter space is added to , + // while the other elements are not changed + oldList.add(prevBox); + // TODO check if this is the correct place; merge was not correct + oldList.addFirst((KnuthBox) removeLast()); + oldList.addFirst((KnuthGlue) removeLast()); + oldList.addFirst((KnuthPenalty) 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 { @@ -546,7 +583,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager } //PHASE 2: Create line breaks - return findOptimalLineBreakingPoints(alignment); + return createLineBreaks(alignment); /* LineBreakPosition lbp = null; if (breakpoints == null) { @@ -586,19 +623,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 +642,85 @@ 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); - } else { - // prevBox is the last element - // in the sub-sequence - // - // the letter space is added to , - // 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()); + // does the first element of the first paragraph add to an existing word? + if (lastPar != null) { + KnuthSequence firstSeq = (KnuthSequence) returnedList.getFirst(); + if (!firstSeq.isInlineSequence()) { + log.error("Expect inline sequence as first sequence when last paragraph is not null"); } - // 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()); + KnuthElement thisElement = (KnuthElement) firstSeq.get(0); + 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 in returnedList + ListIterator iter = returnedList.listIterator(); + while (iter.hasNext()) { + KnuthSequence sequence = (KnuthSequence) iter.next(); + // the sequence contains inline Knuth elements + if (sequence.isInlineSequence()) { + // look at the last element + KnuthElement lastElement = (KnuthElement) sequence.getLast(); + 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(" +"); + } + } + 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(); + 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 +728,15 @@ public class LineLayoutManager extends InlineStackingLayoutManager // if there are other children } } - knuthPar.endParagraph(); - ElementListObserver.observe(knuthPar, "line", null); + if (lastPar != null) { + lastPar.endParagraph(); + if (log.isTraceEnabled()) { + trace.append(" ]"); + } + } + log.trace(trace); + // TODO Do we need more of these lines? + ElementListObserver.observe(lastPar, "line", null); } /** @@ -822,121 +871,160 @@ public class LineLayoutManager extends InlineStackingLayoutManager /** * Phase 2 of Knuth algorithm: find optimal break points. - * @param alignment alignmenr of the paragraph + * @param alignment alignment 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) { + 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, + 0, 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; + } + private LinkedList postProcessLineBreaks(int alignment) { LinkedList returnList = new LinkedList(); @@ -949,8 +1037,9 @@ public class LineLayoutManager extends InlineStackingLayoutManager } lineLayouts = (LineLayoutPossibilities)lineLayoutsList.get(p); - - if (alignment == EN_JUSTIFY) { + KnuthSequence seq = (KnuthSequence) knuthParagraphs.get(p); + + 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 +1064,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 +1503,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; @@ -1419,43 +1519,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 +1610,72 @@ public class LineLayoutManager extends InlineStackingLayoutManager lineArea.setBPD(lineArea.getBPD() + context.getSpaceAfter()); } parentLM.addChildArea(lineArea); - } else { +/* } else if (pos instanceof NonLeafPosition) { + // LineBreakPosition inside a nested block; + // 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.getPos(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)); + } + 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..77a70e9bf 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, @@ -593,7 +597,7 @@ public class TextLayoutManager extends LeafNodeLayoutManager { } } // end of while setFinished(true); - if (returnList.size() > 0) { + if (sequence.size() > 0) { return returnList; } else { return null; diff --git a/src/java/org/apache/fop/render/AbstractRenderer.java b/src/java/org/apache/fop/render/AbstractRenderer.java index 5a08e6082..06fd6625e 100644 --- a/src/java/org/apache/fop/render/AbstractRenderer.java +++ b/src/java/org/apache/fop/render/AbstractRenderer.java @@ -52,6 +52,7 @@ import org.apache.fop.area.inline.Container; import org.apache.fop.area.inline.ForeignObject; import org.apache.fop.area.inline.Image; 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.area.inline.Leader; import org.apache.fop.area.inline.Space; @@ -116,22 +117,22 @@ public abstract class AbstractRenderer } /** - * @see org.apache.fop.render.Renderer + * @see org.apache.fop.render.Renderer#setupFontInfo(FontInfo) */ public abstract void setupFontInfo(FontInfo fontInfo); /** - * @see org.apache.fop.render.Renderer + * @see org.apache.fop.render.Renderer#setUserAgent(FOUserAgent) */ public void setUserAgent(FOUserAgent agent) { userAgent = agent; } - /** @see org.apache.fop.render.Renderer */ + /** @see org.apache.fop.render.Renderer#startRenderer(OutputStream) */ public void startRenderer(OutputStream outputStream) throws IOException { } - /** @see org.apache.fop.render.Renderer */ + /** @see org.apache.fop.render.Renderer#stopRenderer() */ public void stopRenderer() throws IOException { } @@ -148,7 +149,7 @@ public abstract class AbstractRenderer } /** - * @see org.apache.fop.render.Renderer + * @see org.apache.fop.render.Renderer#processOffDocumentItem(OffDocumentItem) */ public void processOffDocumentItem(OffDocumentItem oDI) { } @@ -159,7 +160,7 @@ public abstract class AbstractRenderer * page should not be rendered. The page will be rendered at a later time * by the call to render page. * - * @see org.apache.fop.render.Renderer + * @see org.apache.fop.render.Renderer#preparePage(PageViewport) */ public void preparePage(PageViewport page) { } @@ -196,14 +197,14 @@ public abstract class AbstractRenderer return sb.toString(); } - /** @see org.apache.fop.render.Renderer */ + /** @see org.apache.fop.render.Renderer#startPageSequence(LineArea) */ public void startPageSequence(LineArea seqTitle) { //do nothing } // normally this would be overriden to create a page in the // output - /** @see org.apache.fop.render.Renderer */ + /** @see org.apache.fop.render.Renderer#renderPage(PageViewport) */ public void renderPage(PageViewport page) throws IOException, FOPException { @@ -501,6 +502,7 @@ public abstract class AbstractRenderer + parent.getStartIndent() + line.getStartIndent(); renderLineArea(line); + InlineArea child = (InlineArea) line.getInlineAreas().get(0); currentBPPosition += line.getAllocBPD(); } currentIPPosition = saveIP; @@ -584,6 +586,8 @@ public abstract class AbstractRenderer renderText((TextArea) inlineArea); } else if (inlineArea instanceof InlineParent) { renderInlineParent((InlineParent) inlineArea); + } else if (inlineArea instanceof InlineBlockParent) { + renderInlineBlockParent((InlineBlockParent) inlineArea); } else if (inlineArea instanceof Space) { renderInlineSpace((Space) inlineArea); } else if (inlineArea instanceof Character) { @@ -596,12 +600,10 @@ public abstract class AbstractRenderer } - /** @see org.apache.fop.render.Renderer */ protected void renderCharacter(Character ch) { currentIPPosition += ch.getAllocIPD(); } - /** @see org.apache.fop.render.Renderer */ protected void renderInlineSpace(Space space) { // an inline space moves the inline progression position // for the current block by the width or height of the space @@ -610,17 +612,14 @@ public abstract class AbstractRenderer currentIPPosition += space.getAllocIPD(); } - /** @see org.apache.fop.render.Renderer */ protected void renderLeader(Leader area) { currentIPPosition += area.getAllocIPD(); } - /** @see org.apache.fop.render.Renderer */ protected void renderText(TextArea text) { currentIPPosition += text.getAllocIPD(); } - /** @see org.apache.fop.render.Renderer */ protected void renderInlineParent(InlineParent ip) { int saveIP = currentIPPosition; Iterator iter = ip.getChildAreas().iterator(); @@ -630,7 +629,13 @@ public abstract class AbstractRenderer currentIPPosition = saveIP + ip.getAllocIPD(); } - /** @see org.apache.fop.render.Renderer */ + protected void renderInlineBlockParent(InlineBlockParent ibp) { + // For inline content the BP position is updated by the enclosing line area + int saveBP = currentBPPosition; + renderBlock(ibp.getChildArea()); + currentBPPosition = saveBP; + } + protected void renderViewport(Viewport viewport) { Area content = viewport.getContent(); int saveBP = currentBPPosition; diff --git a/src/java/org/apache/fop/render/xml/XMLRenderer.java b/src/java/org/apache/fop/render/xml/XMLRenderer.java index b1fce13d6..5309fbd55 100644 --- a/src/java/org/apache/fop/render/xml/XMLRenderer.java +++ b/src/java/org/apache/fop/render/xml/XMLRenderer.java @@ -60,6 +60,7 @@ import org.apache.fop.area.inline.Container; import org.apache.fop.area.inline.ForeignObject; import org.apache.fop.area.inline.Image; 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.area.inline.Leader; import org.apache.fop.area.inline.Space; @@ -513,13 +514,14 @@ public class XMLRenderer extends AbstractRenderer { atts.clear(); addAreaAttributes(line); addTraitAttributes(line); + addAttribute("vpos", currentBPPosition); startElement("lineArea", atts); super.renderLineArea(line); endElement("lineArea"); } /** - * @see org.apache.fop.render.Renderer#renderViewport(Viewport) + * @see org.apache.fop.render.AbstractRenderer#renderViewport(Viewport) */ protected void renderViewport(Viewport viewport) { atts.clear(); @@ -544,7 +546,7 @@ public class XMLRenderer extends AbstractRenderer { } /** - * @see org.apache.fop.render.Renderer#renderContainer(Container) + * @see org.apache.fop.render.AbstractRenderer#renderContainer(Container) */ public void renderContainer(Container cont) { startElement("container"); @@ -569,7 +571,7 @@ public class XMLRenderer extends AbstractRenderer { } /** - * @see org.apache.fop.render.Renderer#renderCharacter(Character) + * @see org.apache.fop.render.AbstractRenderer#renderCharacter(Character) */ protected void renderCharacter(org.apache.fop.area.inline.Character ch) { atts.clear(); @@ -581,7 +583,7 @@ public class XMLRenderer extends AbstractRenderer { } /** - * @see org.apache.fop.render.Renderer#renderInlineSpace(Space) + * @see org.apache.fop.render.AbstractRenderer#renderInlineSpace(Space) */ protected void renderInlineSpace(Space space) { atts.clear(); @@ -591,7 +593,7 @@ public class XMLRenderer extends AbstractRenderer { } /** - * @see org.apache.fop.render.Renderer#renderText(TextArea) + * @see org.apache.fop.render.AbstractRenderer#renderText(TextArea) */ protected void renderText(TextArea text) { atts.clear(); @@ -610,7 +612,7 @@ public class XMLRenderer extends AbstractRenderer { } /** - * @see org.apache.fop.render.Renderer#renderInlineParent(InlineParent) + * @see org.apache.fop.render.AbstractRenderer#renderInlineParent(InlineParent) */ protected void renderInlineParent(InlineParent ip) { atts.clear(); @@ -620,8 +622,16 @@ public class XMLRenderer extends AbstractRenderer { endElement("inlineparent"); } + protected void renderInlineBlockParent(InlineBlockParent ibp) { + atts.clear(); + addTraitAttributes(ibp); + startElement("inlineblockparent", atts); + super.renderInlineBlockParent(ibp); + endElement("inlineblockparent"); + } + /** - * @see org.apache.fop.render.Renderer#renderLeader(Leader) + * @see org.apache.fop.render.AbstractRenderer#renderLeader(Leader) */ protected void renderLeader(Leader area) { String style = "solid"; @@ -655,7 +665,7 @@ public class XMLRenderer extends AbstractRenderer { super.renderLeader(area); } - /** @see org.apache.fop.render.AbstractRenderer */ + /** @see org.apache.fop.render.AbstractRenderer#getMimeType() */ public String getMimeType() { return XML_MIME_TYPE; } diff --git a/test/layoutengine/disabled-testcases.txt b/test/layoutengine/disabled-testcases.txt index 1827ae21f..d8a409871 100644 --- a/test/layoutengine/disabled-testcases.txt +++ b/test/layoutengine/disabled-testcases.txt @@ -16,3 +16,5 @@ table-column4.xml table-fixed2.xml font-size-absolute.xml font-size-relative.xml +inline-block1.xml +inline-block2.xml diff --git a/test/layoutengine/testcases/inline-block1.xml b/test/layoutengine/testcases/inline-block1.xml new file mode 100644 index 000000000..0ff64d74d --- /dev/null +++ b/test/layoutengine/testcases/inline-block1.xml @@ -0,0 +1,57 @@ + + + + + +

+ This test checks block content in fo:inline. It stresses the nesting by letting the fo:inline have a background color. +

+
+ + + + + + + + + + + + The appropriate values of the parameters can be obtained from the following equations: + + w1 + w2 + w3 = W, + + + y1 + y2 + y3 = Y, + where W and Y follow from the previous rule. This completes our argument. + + + + + + + +
diff --git a/test/layoutengine/testcases/inline-block2.xml b/test/layoutengine/testcases/inline-block2.xml new file mode 100644 index 000000000..c3b085e70 --- /dev/null +++ b/test/layoutengine/testcases/inline-block2.xml @@ -0,0 +1,75 @@ + + + + + +

+ This test checks block content in fo:inline. It stresses the nesting by letting the fo:inline have a background color. +

+
+ + + + + + + + + + + + The appropriate values of the parameters can be obtained from the following table: + + + + + + + + w1 + w2 + w3 + + + W + + + + + y1 + y2 + y3 + + + Y + + + + + where W and Y follow from the previous rule. This completes our argument. + + + + + + + +
-- 2.39.5