aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/org/apache/fop/layoutmgr
diff options
context:
space:
mode:
authorVincent Hennebert <vhennebert@apache.org>2013-08-29 16:26:12 +0000
committerVincent Hennebert <vhennebert@apache.org>2013-08-29 16:26:12 +0000
commit6a3a80f32459658c5edf5eb1a48f9a59edf5fe1b (patch)
tree0b37aff548c329fb9491f8ccc5cd35aa3c3530e5 /src/java/org/apache/fop/layoutmgr
parentf4b08c8e1f5f6a8901695fe970d66b4ba95496c2 (diff)
downloadxmlgraphics-fop-6a3a80f32459658c5edf5eb1a48f9a59edf5fe1b.tar.gz
xmlgraphics-fop-6a3a80f32459658c5edf5eb1a48f9a59edf5fe1b.zip
FOP-2292: First bits of a whitespace management extension
Patch by Seifeddine Dridi, applied with some modifications (mainly, Java compliance and Checkstyle) git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_WhitespaceManagement@1518691 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java/org/apache/fop/layoutmgr')
-rw-r--r--src/java/org/apache/fop/layoutmgr/AbstractBreaker.java2
-rw-r--r--src/java/org/apache/fop/layoutmgr/AlternativeBlockLayoutManager.java239
-rw-r--r--src/java/org/apache/fop/layoutmgr/AlternativeManager.java150
-rw-r--r--src/java/org/apache/fop/layoutmgr/BestFitLayoutManager.java277
-rw-r--r--src/java/org/apache/fop/layoutmgr/BestFitPenalty.java68
-rw-r--r--src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java40
-rw-r--r--src/java/org/apache/fop/layoutmgr/ElementListUtils.java33
-rw-r--r--src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java1
-rw-r--r--src/java/org/apache/fop/layoutmgr/LayoutContext.java47
-rw-r--r--src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java20
-rw-r--r--src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java1
11 files changed, 857 insertions, 21 deletions
diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
index 51e80e8d4..edd78d5ab 100644
--- a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
+++ b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
@@ -611,7 +611,7 @@ public abstract class AbstractBreaker {
// Handle SpaceHandling(Break)Positions, see SpaceResolver!
SpaceResolver.performConditionalsNotification(effectiveList,
startElementIndex, notificationEndElementIndex, lastBreak);
-
+ childLC.setAlternativeManager(alg.getAlternativeManager());
// Add areas now!
addAreas(new KnuthPossPosIter(effectiveList,
startElementIndex, endElementIndex + 1), childLC);
diff --git a/src/java/org/apache/fop/layoutmgr/AlternativeBlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AlternativeBlockLayoutManager.java
new file mode 100644
index 000000000..1029940a6
--- /dev/null
+++ b/src/java/org/apache/fop/layoutmgr/AlternativeBlockLayoutManager.java
@@ -0,0 +1,239 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Stack;
+
+import org.apache.fop.area.Area;
+import org.apache.fop.area.Block;
+import org.apache.fop.area.LineArea;
+import org.apache.fop.fo.extensions.AlternativeBlock;
+
+/**
+ * Layout manager for {@link AlternativeBlock}
+ */
+
+public class AlternativeBlockLayoutManager extends AbstractLayoutManager {
+
+ private Block curBlockArea;
+
+ public AlternativeBlockLayoutManager(AlternativeBlock node) {
+ super(node);
+ setGeneratesBlockArea(true);
+ }
+
+ /**
+ * Creates and initializes a {@link LayoutContext} to pass to the child LM
+ *
+ * @param context the parent {@link LayoutContext}
+ * @return a new child layout context
+ */
+ protected LayoutContext makeChildLayoutContext(LayoutContext context) {
+ LayoutContext childLC = LayoutContext.newInstance();
+ childLC.copyPendingMarksFrom(context);
+ childLC.setStackLimitBP(context.getStackLimitBP());
+ childLC.setRefIPD(context.getRefIPD());
+ childLC.setWritingMode(context.getWritingMode());
+ return childLC;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public List getNextKnuthElements(LayoutContext context, int alignment) {
+ return getNextKnuthElements(context, alignment, null, null, null);
+ }
+
+ /** {@inheritDoc} */
+ public List getNextKnuthElements(LayoutContext context, int alignment,
+ Stack lmStack, Position positionAtIPDChange,
+ LayoutManager restartAtLM) {
+ LayoutManager curLM;
+ List<ListElement> returnList = new LinkedList<ListElement>();
+ while ((curLM = getChildLM()) != null) {
+ LayoutContext childLC = makeChildLayoutContext(context);
+
+ List returnedList = null;
+ if (!curLM.isFinished()) {
+ returnedList = curLM.getNextKnuthElements(childLC, alignment);
+ }
+ if (returnedList != null) {
+ wrapPositionElements(returnedList, returnList);
+ }
+ }
+ setFinished(true);
+ return returnList;
+ }
+
+ /**
+ * "wrap" the Position inside each element moving the elements from SourceList to targetList
+ *
+ * @param sourceList source list
+ * @param targetList target list receiving the wrapped position elements
+ */
+ protected void wrapPositionElements(List sourceList, List targetList) {
+ wrapPositionElements(sourceList, targetList, false);
+ }
+
+ /**
+ * "wrap" the Position inside each element moving the elements from
+ * SourceList to targetList
+ *
+ * @param sourceList source list
+ * @param targetList target list receiving the wrapped position elements
+ * @param force if true, every Position is wrapped regardless of its LM of origin
+ */
+ protected void wrapPositionElements(List sourceList, List targetList,
+ boolean force) {
+
+ ListIterator listIter = sourceList.listIterator();
+ Object tempElement;
+ while (listIter.hasNext()) {
+ tempElement = listIter.next();
+ if (tempElement instanceof ListElement) {
+ wrapPositionElement((ListElement) tempElement, targetList,
+ force);
+ } else if (tempElement instanceof List) {
+ wrapPositionElements((List) tempElement, targetList, force);
+ }
+ }
+ }
+
+ /**
+ * "wrap" the Position inside the given element and add it to the target list.
+ *
+ * @param el the list element
+ * @param targetList target list receiving the wrapped position elements
+ * @param force if true, every Position is wrapped regardless of its LM of origin
+ */
+ protected void wrapPositionElement(ListElement el, List targetList,
+ boolean force) {
+ if (force || el.getLayoutManager() != this) {
+ el.setPosition(notifyPos(new NonLeafPosition(this, el.getPosition())));
+ }
+ targetList.add(el);
+ }
+
+ @Override
+ public Area getParentArea(Area childArea) {
+ if (curBlockArea == null) {
+ curBlockArea = new Block();
+ // Set up dimensions
+ // Must get dimensions from parent area
+ /*Area parentArea = */parentLayoutManager.getParentArea(curBlockArea);
+ }
+ return curBlockArea;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addChildArea(Area childArea) {
+ if (curBlockArea != null) {
+ if (childArea instanceof LineArea) {
+ curBlockArea.addLineArea((LineArea) childArea);
+ } else {
+ curBlockArea.addBlock((Block) childArea);
+ }
+ }
+ }
+
+ /**
+ * Force current area to be added to parent area.
+ */
+ protected void flush() {
+ if (getCurrentArea() != null) {
+ parentLayoutManager.addChildArea(getCurrentArea());
+ }
+ }
+
+ private Area getCurrentArea() {
+ return curBlockArea;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addAreas(PositionIterator parentIter,
+ LayoutContext layoutContext) {
+
+ getParentArea(null);
+
+ addId();
+
+ LayoutManager childLM;
+ LayoutContext lc = LayoutContext.offspringOf(layoutContext);
+ LayoutManager firstLM = null;
+ LayoutManager lastLM = null;
+ Position firstPos = null;
+ Position lastPos = null;
+
+ // "unwrap" the NonLeafPositions stored in parentIter
+ // and put them in a new list;
+ LinkedList<Position> positionList = new LinkedList<Position>();
+ Position pos;
+ while (parentIter.hasNext()) {
+ pos = parentIter.next();
+ if (pos.getIndex() >= 0) {
+ if (firstPos == null) {
+ firstPos = pos;
+ }
+ lastPos = pos;
+ }
+ if (pos instanceof NonLeafPosition && (pos.getPosition() != null)
+ && pos.getPosition().getLM() != this) {
+ // pos was created by a child of this BestFitBlockLM
+ positionList.add(pos.getPosition());
+ lastLM = pos.getPosition().getLM();
+ if (firstLM == null) {
+ firstLM = lastLM;
+ }
+ }
+ }
+
+ registerMarkers(true, isFirst(firstPos), isLast(lastPos));
+
+ PositionIterator childPosIter = new PositionIterator(
+ positionList.listIterator());
+ while ((childLM = childPosIter.getNextChildLM()) != null) {
+ // Add the block areas to Area
+ // set the space adjustment ratio
+ lc.setSpaceAdjust(layoutContext.getSpaceAdjust());
+ lc.setFlags(LayoutContext.FIRST_AREA, childLM == firstLM);
+ lc.setFlags(LayoutContext.LAST_AREA, childLM == lastLM);
+ lc.setStackLimitBP(layoutContext.getStackLimitBP());
+ childLM.addAreas(childPosIter, lc);
+ }
+
+ registerMarkers(false, isFirst(firstPos), isLast(lastPos));
+
+ flush();
+
+ curBlockArea = null;
+
+ checkEndOfLayout(lastPos);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isRestartable() {
+ return true;
+ }
+
+}
diff --git a/src/java/org/apache/fop/layoutmgr/AlternativeManager.java b/src/java/org/apache/fop/layoutmgr/AlternativeManager.java
new file mode 100644
index 000000000..8ffc96a9e
--- /dev/null
+++ b/src/java/org/apache/fop/layoutmgr/AlternativeManager.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A simple alternative manager that holds a set of {@link Alternative}
+ */
+public class AlternativeManager {
+
+ public static class Alternative {
+
+ /** remaining BPD after inserting the alternative */
+ private int remainingBPD;
+ /** width of the alternative in block-progressing-dimension */
+ private int width;
+ /** Knuth element list */
+ private List<ListElement> knuthList;
+
+ public Alternative(List<ListElement> knuthList, int width) {
+ this.knuthList = knuthList;
+ this.width = width;
+ this.remainingBPD = 0;
+ }
+ public List<ListElement> getKnuthList() {
+ return knuthList;
+ }
+ public int getRemainingBPD() {
+ return remainingBPD;
+ }
+ public int getWidth() {
+ return width;
+ }
+ public void setRemainingBPD(int remainingBPD) {
+ this.remainingBPD = remainingBPD;
+ }
+ }
+
+ public static enum FittingStrategy {
+
+ FIRST_FIT {
+
+ @Override
+ public Alternative filter(List<Alternative> alternatives) {
+ for (Alternative alt : alternatives) {
+ if (alt.getRemainingBPD() > 0) {
+ return alt;
+ }
+ }
+ return null;
+ }
+ },
+
+ SMALLEST_FIT {
+
+ @Override
+ public Alternative filter(List<Alternative> alternatives) {
+ Iterator<Alternative> iter = alternatives.iterator();
+ int biggestDiff = -Integer.MAX_VALUE;
+ Alternative bestAlt = null;
+
+ while (iter.hasNext()) {
+ Alternative alt = iter.next();
+ if (alt.getRemainingBPD() > biggestDiff) {
+ biggestDiff = alt.getRemainingBPD();
+ bestAlt = alt;
+ }
+ }
+ return bestAlt;
+ }
+ },
+
+ BIGGEST_FIT {
+
+ @Override
+ public Alternative filter(List<Alternative> alternatives) {
+ Iterator<Alternative> iter = alternatives.iterator();
+ int smallestDiff = Integer.MAX_VALUE;
+ Alternative bestAlt = null;
+
+ while (iter.hasNext()) {
+ Alternative alt = iter.next();
+ if (alt.getRemainingBPD() < smallestDiff) {
+ smallestDiff = alt.getRemainingBPD();
+ bestAlt = alt;
+ }
+ }
+ return bestAlt;
+ }
+ };
+
+ public abstract Alternative filter(List<Alternative> alternatives);
+
+ }
+
+ private LinkedList<Alternative> alternatives;
+ private LinkedList<Alternative> bestAlternatives;
+
+ public AlternativeManager() {
+ alternatives = new LinkedList<Alternative>();
+ bestAlternatives = new LinkedList<Alternative>();
+ }
+
+ public void addAlternative(Alternative alt) {
+ alternatives.add(alt);
+ }
+
+ public boolean hasAny() {
+ return bestAlternatives.size() > 0;
+ }
+
+ public Alternative getNextBestAlternative() {
+ return bestAlternatives.removeFirst();
+ }
+
+ public Alternative getBestAlternative(FittingStrategy strategy) {
+ try {
+ Alternative bestAlt = strategy.filter(alternatives);
+ if (bestAlt != null) {
+ bestAlternatives.add(bestAlt);
+ }
+ return bestAlt;
+ } catch (NullPointerException e) {
+ return null;
+ } finally {
+ // We don't need the store alternatives anymore
+ alternatives.clear();
+ }
+ }
+}
diff --git a/src/java/org/apache/fop/layoutmgr/BestFitLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BestFitLayoutManager.java
new file mode 100644
index 000000000..aca4d341b
--- /dev/null
+++ b/src/java/org/apache/fop/layoutmgr/BestFitLayoutManager.java
@@ -0,0 +1,277 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Stack;
+
+import org.apache.fop.area.Area;
+import org.apache.fop.area.Block;
+import org.apache.fop.area.LineArea;
+import org.apache.fop.fo.extensions.BestFit;
+import org.apache.fop.layoutmgr.AlternativeManager.Alternative;
+import org.apache.fop.layoutmgr.AlternativeManager.FittingStrategy;
+
+/**
+ * LayoutManager for a fox:best-fit. It selects the "best" alternative
+ * to render based on the chosen fitting strategy.
+ */
+public class BestFitLayoutManager extends AbstractLayoutManager {
+
+ private Block curBlockArea;
+
+ private static class BestFitPosition extends Position {
+
+ public BestFitPosition(LayoutManager lm) {
+ super(lm);
+ }
+
+ }
+
+ private FittingStrategy fittingStrategy;
+
+ public BestFitLayoutManager(BestFit node) {
+ super(node);
+ fittingStrategy = node.getStrategy();
+ setGeneratesBlockArea(true);
+ }
+
+ /**
+ * Creates and initializes a {@link LayoutContext} to pass to the child LM
+ *
+ * @param context the parent {@link LayoutContext}
+ * @return a new child layout context
+ */
+ protected LayoutContext makeChildLayoutContext(LayoutContext context) {
+ LayoutContext childLC = LayoutContext.newInstance();
+ childLC.copyPendingMarksFrom(context);
+ childLC.setStackLimitBP(context.getStackLimitBP());
+ childLC.setRefIPD(context.getRefIPD());
+ childLC.setWritingMode(context.getWritingMode());
+ return childLC;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public List getNextKnuthElements(LayoutContext context, int alignment) {
+ return getNextKnuthElements(context, alignment, null, null, null);
+ }
+
+ /** {@inheritDoc} */
+ public List getNextKnuthElements(LayoutContext context, int alignment,
+ Stack lmStack, Position positionAtIPDChange,
+ LayoutManager restartAtLM) {
+ LayoutManager curLM; // currently active LM
+ BestFitPenalty bestFitPenalty =
+ new BestFitPenalty(fittingStrategy, new BestFitPosition(this));
+ while ((curLM = getChildLM()) != null) {
+ LayoutContext childLC = makeChildLayoutContext(context);
+
+ List returnedList = null;
+ if (!curLM.isFinished()) {
+ returnedList = curLM.getNextKnuthElements(childLC, alignment);
+ }
+ if (returnedList != null) {
+ List<ListElement> returnList = new LinkedList<ListElement>();
+ this.wrapPositionElements(returnedList, returnList);
+ int contentLength = ElementListUtils.calcFullContentLength(returnList);
+ // Add a new variant
+ bestFitPenalty.addAlternative(new Alternative(returnList, contentLength));
+ }
+ }
+ setFinished(true);
+ List<ListElement> finalList = new LinkedList<ListElement>();
+ // Insert an empty Knuth box to prevent the layout engine from ignoring our penalty
+ finalList.add(new KnuthBox(0, new Position(this), false));
+ finalList.add(bestFitPenalty);
+ return finalList;
+ }
+
+ /**
+ * "wrap" the Position inside each element moving the elements from
+ * SourceList to targetList
+ *
+ * @param sourceList source list
+ * @param targetList target list receiving the wrapped position elements
+ */
+ protected void wrapPositionElements(List sourceList, List targetList) {
+ wrapPositionElements(sourceList, targetList, false);
+ }
+
+ /**
+ * "wrap" the Position inside each element moving the elements from
+ * SourceList to targetList
+ *
+ * @param sourceList source list
+ * @param targetList target list receiving the wrapped position elements
+ * @param force if true, every Position is wrapped regardless of its LM of origin
+ */
+ protected void wrapPositionElements(List sourceList, List targetList,
+ boolean force) {
+
+ ListIterator listIter = sourceList.listIterator();
+ Object tempElement;
+ while (listIter.hasNext()) {
+ tempElement = listIter.next();
+ if (tempElement instanceof ListElement) {
+ wrapPositionElement((ListElement) tempElement, targetList,
+ force);
+ } else if (tempElement instanceof List) {
+ wrapPositionElements((List) tempElement, targetList, force);
+ }
+ }
+ }
+
+ /**
+ * "wrap" the Position inside the given element and add it to the target list.
+ *
+ * @param el the list element
+ * @param targetList target list receiving the wrapped position elements
+ * @param force if true, every Position is wrapped regardless of its LM of origin
+ */
+ protected void wrapPositionElement(ListElement el, List targetList,
+ boolean force) {
+ if (force || el.getLayoutManager() != this) {
+ el.setPosition(notifyPos(new NonLeafPosition(this, el.getPosition())));
+ }
+ targetList.add(el);
+ }
+
+ @Override
+ public Area getParentArea(Area childArea) {
+ if (curBlockArea == null) {
+ curBlockArea = new Block();
+ // Set up dimensions
+ // Must get dimensions from parent area
+ /*Area parentArea = */parentLayoutManager.getParentArea(curBlockArea);
+ }
+ return curBlockArea;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addChildArea(Area childArea) {
+ if (curBlockArea != null) {
+ if (childArea instanceof LineArea) {
+ curBlockArea.addLineArea((LineArea) childArea);
+ } else {
+ curBlockArea.addBlock((Block) childArea);
+ }
+ }
+ }
+
+ /**
+ * Force current area to be added to parent area.
+ */
+ protected void flush() {
+ if (getCurrentArea() != null) {
+ parentLayoutManager.addChildArea(getCurrentArea());
+ }
+ }
+
+ private Area getCurrentArea() {
+ return curBlockArea;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addAreas(PositionIterator parentIter,
+ LayoutContext layoutContext) {
+
+ getParentArea(null);
+
+ addId();
+
+ LayoutManager childLM;
+ LayoutContext lc = LayoutContext.offspringOf(layoutContext);
+ LayoutManager firstLM = null;
+ LayoutManager lastLM = null;
+ Position firstPos = null;
+ Position lastPos = null;
+
+ // "unwrap" the NonLeafPositions stored in parentIter
+ // and put them in a new list;
+ LinkedList<Position> positionList = new LinkedList<Position>();
+ Position pos;
+ AlternativeManager altManager = layoutContext.getAlternativeManager();
+ while (parentIter.hasNext()) {
+ pos = parentIter.next();
+ if (pos.getIndex() >= 0) {
+ if (firstPos == null) {
+ firstPos = pos;
+ }
+ lastPos = pos;
+ }
+ if (pos instanceof NonLeafPosition && (pos.getPosition() != null)
+ && pos.getPosition().getLM() != this) {
+ // pos was created by a child of this BestFitBlockLM
+ positionList.add(pos.getPosition());
+ lastLM = pos.getPosition().getLM();
+ if (firstLM == null) {
+ firstLM = lastLM;
+ }
+ }
+ else if (pos instanceof BestFitPosition) {
+ if (altManager.hasAny()) {
+ Alternative bestAlt = altManager.getNextBestAlternative();
+ Iterator<ListElement> it = bestAlt.getKnuthList().iterator();
+ while (it.hasNext()) {
+ positionList.add(it.next().getPosition());
+ }
+ }
+ lastLM = pos.getLM();
+ if (firstLM == null) {
+ firstLM = lastLM;
+ }
+ }
+ }
+
+ //registerMarkers(true, isFirst(firstPos), isLast(lastPos));
+
+ PositionIterator childPosIter = new PositionIterator(
+ positionList.listIterator());
+ while ((childLM = childPosIter.getNextChildLM()) != null) {
+ // Add the block areas to Area
+ // set the space adjustment ratio
+ lc.setSpaceAdjust(layoutContext.getSpaceAdjust());
+ lc.setFlags(LayoutContext.FIRST_AREA, childLM == firstLM);
+ lc.setFlags(LayoutContext.LAST_AREA, childLM == lastLM);
+ lc.setStackLimitBP(layoutContext.getStackLimitBP());
+ childLM.addAreas(childPosIter, lc);
+ }
+
+ //registerMarkers(false, isFirst(firstPos), isLast(lastPos));
+
+ flush();
+
+ curBlockArea = null;
+
+ checkEndOfLayout(lastPos);
+ }
+
+ /** {@inheritDoc} */
+ public boolean isRestartable() {
+ return true;
+ }
+
+}
diff --git a/src/java/org/apache/fop/layoutmgr/BestFitPenalty.java b/src/java/org/apache/fop/layoutmgr/BestFitPenalty.java
new file mode 100644
index 000000000..6a9658d2a
--- /dev/null
+++ b/src/java/org/apache/fop/layoutmgr/BestFitPenalty.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import java.util.LinkedList;
+
+import org.apache.fop.layoutmgr.AlternativeManager.Alternative;
+import org.apache.fop.layoutmgr.AlternativeManager.FittingStrategy;
+
+/**
+ * A dummy penalty used in {@link BestFitLayoutManager} to store
+ * the different alternatives in {@link fox:best-fit}
+ */
+
+public class BestFitPenalty extends KnuthPenalty {
+
+ private LinkedList<Alternative> alternatives;
+ private FittingStrategy strategy;
+
+ public BestFitPenalty(FittingStrategy strategy, Position pos) {
+ super(0, 0, false, pos, false);
+ this.strategy = strategy;
+ alternatives = new LinkedList<Alternative>();
+ }
+
+ public void addAlternative(Alternative alternative) {
+ alternatives.add(alternative);
+ }
+
+ public Alternative getAlternative(int index) {
+ return alternatives.get(index);
+ }
+
+ public int getAlternativeCount() {
+ return alternatives.size();
+ }
+
+ public FittingStrategy getStrategyType() {
+ return strategy;
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ String str = super.toString();
+ StringBuffer buffer = new StringBuffer(64);
+ buffer.append(" number of alternatives = " + getAlternativeCount());
+ buffer.append(" fitting-strategy = " + strategy);
+ return str + buffer.toString();
+ }
+
+}
diff --git a/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java
index c7048295f..54d68c3d8 100644
--- a/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java
+++ b/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java
@@ -23,6 +23,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.fo.Constants;
+import org.apache.fop.layoutmgr.AlternativeManager.Alternative;
/**
* The set of nodes is sorted into lines indexed into activeLines.
@@ -192,6 +193,8 @@ public abstract class BreakingAlgorithm {
private boolean partOverflowRecoveryActivated = true;
private KnuthNode lastRecovered;
+ private AlternativeManager altManager = new AlternativeManager();
+
/**
* Create a new instance.
*
@@ -822,6 +825,30 @@ public abstract class BreakingAlgorithm {
}
}
+ private int handleBestFitPenalty(BestFitPenalty penalty, KnuthNode node,
+ int elementIdx) {
+ int difference;
+ // Find the alternatives that can be fitted inside the remaining space
+ for (int i = 0; i < penalty.getAlternativeCount(); ++i) {
+ Alternative alt = penalty.getAlternative(i);
+ difference = computeDifference(node, new KnuthPenalty(alt.getWidth(),
+ 0, false, null, false), elementIdx);
+ if (difference > 0) {
+ alt.setRemainingBPD(difference);
+ altManager.addAlternative(alt);
+ }
+ }
+ Alternative bestAlt = altManager.getBestAlternative(penalty.getStrategyType());
+ if (bestAlt != null) {
+ difference = bestAlt.getRemainingBPD();
+ return difference;
+ } else {
+ // No alternative can be fitted inside the remaining space
+ return computeDifference(node, new KnuthPenalty(0,
+ 0, false, null, false), elementIdx);
+ }
+ }
+
/**
* Replace the last too-long or too-short node by the last deactivated
* node, if applicable.
@@ -930,7 +957,13 @@ public abstract class BreakingAlgorithm {
if (node.position == elementIdx) {
continue;
}
- int difference = computeDifference(node, element, elementIdx);
+ int difference;
+ if (element instanceof BestFitPenalty) {
+ BestFitPenalty penalty = (BestFitPenalty)element;
+ difference = handleBestFitPenalty(penalty, node, elementIdx);
+ } else {
+ difference = computeDifference(node, element, elementIdx);
+ }
if (!elementCanEndLine(element, endLine, difference)) {
log.trace("Skipping legal break");
break;
@@ -1451,4 +1484,9 @@ public abstract class BreakingAlgorithm {
return this.alignmentLast;
}
+ /** @return the alternative manager (used by fox:best-fit) */
+ public AlternativeManager getAlternativeManager() {
+ return altManager;
+ }
+
}
diff --git a/src/java/org/apache/fop/layoutmgr/ElementListUtils.java b/src/java/org/apache/fop/layoutmgr/ElementListUtils.java
index 2bd6f429a..d16bd3117 100644
--- a/src/java/org/apache/fop/layoutmgr/ElementListUtils.java
+++ b/src/java/org/apache/fop/layoutmgr/ElementListUtils.java
@@ -163,6 +163,37 @@ public final class ElementListUtils {
}
/**
+ * Calculates the full content length of the given element list. Warning: It doesn't take any
+ * stretch and shrink possibilities into account.
+ * @param elems the element list
+ * @return the content length
+ */
+ public static int calcFullContentLength(List elems) {
+ ListIterator iter = elems.listIterator(0);
+ int count = elems.size();
+ int len = 0;
+ while (iter.hasNext()) {
+ ListElement el = (ListElement)iter.next();
+ if (el.isBox()) {
+ len += ((KnuthElement)el).getWidth();
+ } else if (el.isGlue()) {
+ len += ((KnuthElement)el).getWidth();
+ }
+ else if (el.isUnresolvedElement() && (el instanceof BreakElement) == false) {
+ // Handle properties space-before and space-after
+ UnresolvedListElementWithLength uresolvedEl = (UnresolvedListElementWithLength)el;
+ if (uresolvedEl.isLast() || uresolvedEl.isFirst())
+ len += uresolvedEl.getLength().getOpt();
+ }
+ count--;
+ if (count == 0) {
+ break;
+ }
+ }
+ return len;
+ }
+
+ /**
* Calculates the content length of the given element list. Warning: It doesn't take any
* stretch and shrink possibilities into account.
* @param elems the element list
@@ -201,7 +232,7 @@ public final class ElementListUtils {
if (last.isPenalty() && ((KnuthPenalty)last).getPenalty() < KnuthElement.INFINITE) {
return true;
} else if (last instanceof BreakElement
- && ((BreakElement)last).getPenaltyValue() < KnuthElement.INFINITE) {
+ && ((BreakElement)last).getPenaltyValue() < KnuthElement.INFINITE) {
return true;
}
return false;
diff --git a/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java b/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java
index 0260046e1..ae99ba578 100644
--- a/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java
@@ -204,6 +204,7 @@ public class FlowLayoutManager extends BlockStackingLayoutManager
childLC.setStackLimitBP(context.getStackLimitBP());
childLC.setRefIPD(context.getRefIPD());
childLC.setWritingMode(getCurrentPage().getSimplePageMaster().getWritingMode());
+ childLC.setAlternativeManager(context.getAlternativeManager());
return childLC;
}
diff --git a/src/java/org/apache/fop/layoutmgr/LayoutContext.java b/src/java/org/apache/fop/layoutmgr/LayoutContext.java
index 21983c20f..3120b147e 100644
--- a/src/java/org/apache/fop/layoutmgr/LayoutContext.java
+++ b/src/java/org/apache/fop/layoutmgr/LayoutContext.java
@@ -23,6 +23,7 @@ import java.util.Collections;
import java.util.List;
import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.extensions.AlternativeBlock;
import org.apache.fop.layoutmgr.inline.AlignmentContext;
import org.apache.fop.layoutmgr.inline.HyphContext;
import org.apache.fop.traits.MinOptMax;
@@ -35,6 +36,9 @@ import org.apache.fop.traits.WritingMode;
*/
public final class LayoutContext {
+ /** Manager of @{link {@link AlternativeBlock} and only used by {@link BestFitLayoutManager}*/
+ private AlternativeManager altManager;
+
/** Generated break possibility is first in a new area */
public static final int NEW_AREA = 0x01;
@@ -142,6 +146,7 @@ public final class LayoutContext {
public static LayoutContext offspringOf(LayoutContext parent) {
LayoutContext offspring = new LayoutContext(0);
offspring.setTreatAsArtifact(parent.treatAsArtifact());
+ offspring.setAlternativeManager(parent.getAlternativeManager());
return offspring;
}
@@ -646,24 +651,24 @@ public final class LayoutContext {
/** {@inheritDoc} */
public String toString() {
return "Layout Context:"
- + "\nStack Limit BPD: \t"
- + (getStackLimitBP() == null ? "null" : getStackLimitBP().toString())
- + "\nTrailing Space: \t"
- + (getTrailingSpace() == null ? "null" : getTrailingSpace().toString())
- + "\nLeading Space: \t"
- + (getLeadingSpace() == null ? "null" : getLeadingSpace().toString())
- + "\nReference IPD: \t" + getRefIPD()
- + "\nSpace Adjust: \t" + getSpaceAdjust()
- + "\nIPD Adjust: \t" + getIPDAdjust()
- + "\nResolve Leading Space: \t" + resolveLeadingSpace()
- + "\nSuppress Break Before: \t" + suppressBreakBefore()
- + "\nIs First Area: \t" + isFirstArea()
- + "\nStarts New Area: \t" + startsNewArea()
- + "\nIs Last Area: \t" + isLastArea()
- + "\nKeeps: \t[keep-with-next=" + getKeepWithNextPending()
+ + "\nStack Limit BPD: \t"
+ + (getStackLimitBP() == null ? "null" : getStackLimitBP().toString())
+ + "\nTrailing Space: \t"
+ + (getTrailingSpace() == null ? "null" : getTrailingSpace().toString())
+ + "\nLeading Space: \t"
+ + (getLeadingSpace() == null ? "null" : getLeadingSpace().toString())
+ + "\nReference IPD: \t" + getRefIPD()
+ + "\nSpace Adjust: \t" + getSpaceAdjust()
+ + "\nIPD Adjust: \t" + getIPDAdjust()
+ + "\nResolve Leading Space: \t" + resolveLeadingSpace()
+ + "\nSuppress Break Before: \t" + suppressBreakBefore()
+ + "\nIs First Area: \t" + isFirstArea()
+ + "\nStarts New Area: \t" + startsNewArea()
+ + "\nIs Last Area: \t" + isLastArea()
+ + "\nKeeps: \t[keep-with-next=" + getKeepWithNextPending()
+ "][keep-with-previous=" + getKeepWithPreviousPending() + "] pending"
- + "\nBreaks: \tforced [" + (breakBefore != Constants.EN_AUTO ? "break-before" : "") + "]["
- + (breakAfter != Constants.EN_AUTO ? "break-after" : "") + "]";
+ + "\nBreaks: \tforced [" + (breakBefore != Constants.EN_AUTO ? "break-before" : "") + "]["
+ + (breakAfter != Constants.EN_AUTO ? "break-after" : "") + "]";
}
/**
@@ -692,5 +697,13 @@ public final class LayoutContext {
public void setTreatAsArtifact(boolean treatAsArtifact) {
setFlags(TREAT_AS_ARTIFACT, treatAsArtifact);
}
+
+ public void setAlternativeManager(AlternativeManager altManager) {
+ this.altManager = altManager;
+ }
+
+ public AlternativeManager getAlternativeManager() {
+ return altManager;
+ }
}
diff --git a/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java b/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java
index 292251a84..2d08e674c 100644
--- a/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java
+++ b/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java
@@ -34,6 +34,8 @@ import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FONode.FONodeIterator;
import org.apache.fop.fo.FOText;
import org.apache.fop.fo.FObjMixed;
+import org.apache.fop.fo.extensions.AlternativeBlock;
+import org.apache.fop.fo.extensions.BestFit;
import org.apache.fop.fo.extensions.ExternalDocument;
import org.apache.fop.fo.flow.BasicLink;
import org.apache.fop.fo.flow.BidiOverride;
@@ -142,6 +144,8 @@ public class LayoutManagerMapping implements LayoutManagerMaker {
registerMaker(TableHeader.class, new Maker());
registerMaker(Wrapper.class, new WrapperLayoutManagerMaker());
registerMaker(Title.class, new InlineLayoutManagerMaker());
+ registerMaker(BestFit.class, new BestFitLayoutManagerMaker());
+ registerMaker(AlternativeBlock.class, new AlternativeBlockLayoutManagerMaker());
}
/**
@@ -445,4 +449,20 @@ public class LayoutManagerMapping implements LayoutManagerMaker {
}
}
+ /** a layout manager maker */
+ public class BestFitLayoutManagerMaker extends Maker {
+ /** {@inheritDoc} */
+ public void make(FONode node, List lms) {
+ lms.add(new BestFitLayoutManager((BestFit) node));
+ }
+ }
+
+ /** a layout manager maker */
+ public class AlternativeBlockLayoutManagerMaker extends Maker {
+ /** {@inheritDoc} */
+ public void make(FONode node, List lms) {
+ lms.add(new AlternativeBlockLayoutManager((AlternativeBlock) node));
+ }
+ }
+
}
diff --git a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
index 59145dd72..81aff8845 100644
--- a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
+++ b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
@@ -26,7 +26,6 @@ import java.util.ListIterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FObj;
import org.apache.fop.layoutmgr.AbstractBreaker.PageBreakPosition;