diff options
author | Vincent Hennebert <vhennebert@apache.org> | 2013-11-07 20:49:18 +0000 |
---|---|---|
committer | Vincent Hennebert <vhennebert@apache.org> | 2013-11-07 20:49:18 +0000 |
commit | 7c9187955e5ae1c8181a52715791e4b61fc977bb (patch) | |
tree | 272804e274ce56b5374c6c31b7ade6fc2693ec59 /src/java | |
parent | fb7bda27d7c52920c6234d0f73dd2e423dce735a (diff) | |
download | xmlgraphics-fop-7c9187955e5ae1c8181a52715791e4b61fc977bb.tar.gz xmlgraphics-fop-7c9187955e5ae1c8181a52715791e4b61fc977bb.zip |
Implement whitespace management extension using fo:multi-switch
Patch from 13/10/28 by Seifeddine Dridi, applied with some modifications.
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_WhitespaceManagement@1539809 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/java')
20 files changed, 762 insertions, 812 deletions
diff --git a/src/java/org/apache/fop/fo/Constants.java b/src/java/org/apache/fop/fo/Constants.java index 9cec6e566..d285744e4 100644 --- a/src/java/org/apache/fop/fo/Constants.java +++ b/src/java/org/apache/fop/fo/Constants.java @@ -816,11 +816,14 @@ public interface Constants { /** Scope for table header */ int PR_X_HEADER_COLUMN = 290; - /** Property constant - FOP proprietary*/ + /** Property constant */ int PR_X_FITTING_STRATEGY = 291; + /** Property constant */ + int PR_X_AUTO_TOGGLE = 292; + /** Number of property constants defined */ - int PROPERTY_COUNT = 291; + int PROPERTY_COUNT = 292; // compound property constants diff --git a/src/java/org/apache/fop/fo/FOPropertyMapping.java b/src/java/org/apache/fop/fo/FOPropertyMapping.java index 656d5c06c..31166abd7 100644 --- a/src/java/org/apache/fop/fo/FOPropertyMapping.java +++ b/src/java/org/apache/fop/fo/FOPropertyMapping.java @@ -2128,7 +2128,7 @@ public final class FOPropertyMapping implements Constants { addPropertyMaker("starting-state", m); // switch-to - m = new ToBeImplementedProperty.Maker(PR_SWITCH_TO); + m = new StringProperty.Maker(PR_SWITCH_TO); m.setInherited(false); m.setDefault("xsl-any"); addPropertyMaker("switch-to", m); @@ -2617,12 +2617,18 @@ public final class FOPropertyMapping implements Constants { m.setDefault(""); addPropertyMaker("fox:alt-text", m); - //fox:fitting-strategy, used only in fox:best-fit + // fox:fitting-strategy, used only in fox:best-fit m = new StringProperty.Maker(PR_X_FITTING_STRATEGY); m.setInherited(false); m.setDefault("first-fit"); addPropertyMaker("fox:fitting-strategy", m); + // fox:auto-toggle, used only in fo:multi-switch + m = new StringProperty.Maker(PR_X_AUTO_TOGGLE); + m.setInherited(false); + m.setDefault(""); + addPropertyMaker("fox:auto-toggle", m); + // fox:border-*-radius-* m = new CondLengthProperty.Maker(PR_X_BORDER_BEFORE_RADIUS_START); m.useGeneric(genericCondCornerRadius); diff --git a/src/java/org/apache/fop/fo/extensions/AlternativeBlock.java b/src/java/org/apache/fop/fo/extensions/AlternativeBlock.java deleted file mode 100644 index 3e10e73cf..000000000 --- a/src/java/org/apache/fop/fo/extensions/AlternativeBlock.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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.fo.extensions; - -import org.xml.sax.Attributes; -import org.xml.sax.Locator; - -import org.apache.fop.apps.FOPException; -import org.apache.fop.fo.FONode; -import org.apache.fop.fo.FObj; -import org.apache.fop.fo.PropertyList; -import org.apache.fop.fo.ValidationException; - -/** - * A class modeling fox:alternative-block - */ - -public class AlternativeBlock extends FObj { - - public static final String OBJECT_NAME = "alternative-block"; - - public AlternativeBlock(FONode parent) { - super(parent); - } - - @Override - public void processNode(String elementName, Locator locator, - Attributes attlist, PropertyList pList) throws FOPException { - if (log.isDebugEnabled()) { - log.debug("org.apache.fop.fo.extensions.AlternativeBlock: " + elementName - + (locator != null ? " at " + getLocatorString(locator) : "")); - } - } - - @Override - public void startOfNode() throws FOPException { - if (log.isDebugEnabled()) { - log.debug("AlternativeBlock.startOfNode()"); - } - } - - @Override - public void endOfNode() throws FOPException { - if (log.isDebugEnabled()) { - log.debug("AlternativeBlock.endOfNode()"); - } - } - - /** - * Content model: see {@link Block} - */ - @Override - protected void validateChildNode(Locator loc, String nsURI, String localName) - throws ValidationException { - if (FOX_URI.equals(nsURI)) { - if ("best-fit".equals(localName) || "alternative-block".equals(localName)) { - invalidChildError(loc, FOX_URI, localName); - } - } else if (FO_URI.equals(nsURI)) { - if (!isBlockOrInlineItem(nsURI, localName)) { - invalidChildError(loc, nsURI, localName); - } - } - } - - @Override - public String getLocalName() { - return OBJECT_NAME; - } - - @Override - public String getNormalNamespacePrefix() { - return ExtensionElementMapping.STANDARD_PREFIX; - } - - @Override - public String getNamespaceURI() { - return ExtensionElementMapping.URI; - } - -} diff --git a/src/java/org/apache/fop/fo/extensions/BestFit.java b/src/java/org/apache/fop/fo/extensions/BestFit.java index 4b5275f2a..461dd7a26 100644 --- a/src/java/org/apache/fop/fo/extensions/BestFit.java +++ b/src/java/org/apache/fop/fo/extensions/BestFit.java @@ -15,8 +15,6 @@ * limitations under the License. */ -/* $Id$ */ - package org.apache.fop.fo.extensions; import org.xml.sax.Locator; @@ -24,72 +22,36 @@ import org.xml.sax.Locator; import org.apache.fop.apps.FOPException; import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; -import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; +import org.apache.fop.fo.flow.MultiCaseHandler; +import org.apache.fop.fo.flow.MultiSwitch; import org.apache.fop.layoutmgr.Alternative.FittingStrategy; -/** - * A class that holds a set of <fox:alternative-block> blocks where each one is examined, - * and the one that best matches the fitting strategy is selected. - * The selected alternative should have an occupied BPD that is less than - * the remaining BPD of the current page. - */ -public class BestFit extends FObj { +public class BestFit extends FObj implements MultiCaseHandler { public static final String OBJECT_NAME = "best-fit"; - private FittingStrategy strategy = FittingStrategy.FIRST_FIT; + private FittingStrategy strategy; public BestFit(FONode parent) { super(parent); - } - /** {@inheritDoc} */ - @Override - public void bind(PropertyList pList) throws FOPException { - super.bind(pList); - String strategyName = pList.get(PR_X_FITTING_STRATEGY).getString(); - for (FittingStrategy fs : FittingStrategy.values()) { - if (fs.getStrategyName().equals(strategyName)) { - strategy = fs; - return; + public void setFittingStrategy(String strategyName) { + strategy = FittingStrategy.make(strategyName); + if (strategy == null) { + strategy = FittingStrategy.FIRST_FIT; + if (log.isWarnEnabled()) { + log.warn("Unrecognized strategy name => " + strategyName + ". Using default strategy (first-fit"); } } - if (log.isWarnEnabled()) { - log.warn("Unrecognized strategy name => " + strategyName + ". Using default strategy (first-fit"); - } - } - - @Override - public void startOfNode() throws FOPException { - super.startOfNode(); - if (log.isDebugEnabled()) { - log.debug("BestFit.startOfNode()"); - } - } - - @Override - public void endOfNode() throws FOPException { - super.endOfNode(); - if (log.isDebugEnabled()) { - log.debug("BestFit.endOfNode()"); - } } /** * {@inheritDoc} - * Content model: (fox:alternative-block)+ */ @Override protected void validateChildNode(Locator loc, String nsURI, String localName) throws ValidationException { - if (FOX_URI.equals(nsURI)) { - if (!"alternative-block".equals(localName)) { - invalidChildError(loc, FOX_URI, localName); - } - } else { - invalidChildError(loc, nsURI, localName); - } } @Override @@ -107,8 +69,19 @@ public class BestFit extends FObj { return ExtensionElementMapping.URI; } + public void filter(MultiSwitch multiSwitch) throws FOPException { + // Modifying the FO tree is not advised... +// FONodeIterator nodeIter = multiSwitch.getChildNodes(); +// while (nodeIter.hasNext()) { +// FONode childNode = (FONode) nodeIter.next(); +// this.addChildNode(childNode); +// } +// multiSwitch.clearChildNodes(); + } + public FittingStrategy getStrategy() { return strategy; } } + diff --git a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java index f6f681acc..6bd90784f 100644 --- a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java +++ b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java @@ -50,6 +50,7 @@ public class ExtensionElementMapping extends ElementMapping { PROPERTY_ATTRIBUTES.add("internal-destination"); PROPERTY_ATTRIBUTES.add("disable-column-balancing"); PROPERTY_ATTRIBUTES.add("fitting-strategy"); + PROPERTY_ATTRIBUTES.add("auto-toggle"); //These are FOP's extension properties for accessibility PROPERTY_ATTRIBUTES.add("alt-text"); PROPERTY_ATTRIBUTES.add("header"); @@ -87,8 +88,6 @@ public class ExtensionElementMapping extends ElementMapping { foObjs.put("label", new UnknownXMLObj.Maker(URI)); foObjs.put("destination", new DestinationMaker()); foObjs.put("external-document", new ExternalDocumentMaker()); - foObjs.put("best-fit", new BestFitMaker()); - foObjs.put("alternative-block", new AlternativeBlockMaker()); } } @@ -106,20 +105,6 @@ public class ExtensionElementMapping extends ElementMapping { } } - static class BestFitMaker extends ElementMapping.Maker { - @Override - public FONode make(FONode parent) { - return new BestFit(parent); - } - } - - static class AlternativeBlockMaker extends ElementMapping.Maker { - @Override - public FONode make(FONode parent) { - return new AlternativeBlock(parent); - } - } - /** {@inheritDoc} */ @Override public String getStandardPrefix() { diff --git a/src/java/org/apache/fop/fo/flow/MultiCase.java b/src/java/org/apache/fop/fo/flow/MultiCase.java index ac965dffb..053e0d6f2 100644 --- a/src/java/org/apache/fop/fo/flow/MultiCase.java +++ b/src/java/org/apache/fop/fo/flow/MultiCase.java @@ -19,10 +19,13 @@ package org.apache.fop.fo.flow; +import org.xml.sax.Locator; + import org.apache.fop.apps.FOPException; import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; +import org.apache.fop.fo.ValidationException; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_multi-case"> @@ -32,6 +35,9 @@ import org.apache.fop.fo.PropertyList; public class MultiCase extends FObj { // The value of properties relevant for fo:multi-case. private int startingState; + private String caseName; + private String caseTitle; + private MultiCaseHandler multiCaseHandler; // private ToBeImplementedProperty caseName; // private ToBeImplementedProperty caseTitle; // Unused but valid items, commented out for performance: @@ -55,12 +61,50 @@ public class MultiCase extends FObj { } } - /** {@inheritDoc} */ + @Override public void bind(PropertyList pList) throws FOPException { super.bind(pList); startingState = pList.get(PR_STARTING_STATE).getEnum(); - // caseName = pList.get(PR_CASE_NAME); - // caseTitle = pList.get(PR_CASE_TITLE); + caseName = pList.get(PR_CASE_NAME).getString(); + caseTitle = pList.get(PR_CASE_TITLE).getString(); + if (startingState == EN_SHOW) { + MultiSwitch multiSwitch = (MultiSwitch) parent; + if (multiSwitch.getCurrentlyVisibleNode() == null) { + multiSwitch.setCurrentlyVisibleNode(this); + } + } + } + + /** + * Content Model: (#PCDATA|%inline;|%block)* + */ + @Override + protected void validateChildNode(Locator loc, String nsURI, String localName) + throws ValidationException { + if (FO_URI.equals(nsURI)) { + if (!isBlockOrInlineItem(nsURI, localName) || "marker".equals(localName)) { + invalidChildError(loc, nsURI, localName); + } + if (!"multi-toggle".equals(localName)) { + // Validate against parent of fo:multi-switch + FONode.validateChildNode(getParent().getParent(), loc, nsURI, localName); + } + } + } + + @Override + public void endOfNode() throws FOPException { + if (firstChild == null) { + missingChildElementError("(#PCDATA|%inline;|%block)*"); + } + } + + @Override + protected void addChildNode(FONode child) throws FOPException { + if (child instanceof MultiCaseHandler) { + multiCaseHandler = (MultiCaseHandler) child; + } + super.addChildNode(child); } /** @return the "starting-state" property */ @@ -68,7 +112,7 @@ public class MultiCase extends FObj { return startingState; } - /** {@inheritDoc} */ + @Override public String getLocalName() { return "multi-case"; } @@ -77,7 +121,25 @@ public class MultiCase extends FObj { * {@inheritDoc} * @return {@link org.apache.fop.fo.Constants#FO_MULTI_CASE} */ + @Override public int getNameId() { return FO_MULTI_CASE; } + + public String getCaseName() { + return caseName; + } + + public String getCaseTitle() { + return caseTitle; + } + + public boolean isActuated() { + return multiCaseHandler != null; + } + + MultiCaseHandler getHandler() { + return multiCaseHandler; + } + } diff --git a/src/java/org/apache/fop/fo/flow/MultiCaseHandler.java b/src/java/org/apache/fop/fo/flow/MultiCaseHandler.java new file mode 100644 index 000000000..25a11bd68 --- /dev/null +++ b/src/java/org/apache/fop/fo/flow/MultiCaseHandler.java @@ -0,0 +1,38 @@ +/* + * 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. + */ + +package org.apache.fop.fo.flow; + +import org.apache.fop.apps.FOPException; + +/** + * This interface is used in conjunction with {@link MultiSwitch} + * to implement a callback system for handling fo:multi-case elements. + */ + +public interface MultiCaseHandler { + + /** + * A filtering function used to select one or more fo:multi-case + * elements, children of {@link MultiSwitch}. + * + * @param multiSwitch parent of the fo:multi-case elements to filter + * @throws FOPException + */ + void filter(MultiSwitch multiSwitch) throws FOPException; + +} diff --git a/src/java/org/apache/fop/fo/flow/MultiSwitch.java b/src/java/org/apache/fop/fo/flow/MultiSwitch.java index d8ebee4cb..0c1bb2cde 100644 --- a/src/java/org/apache/fop/fo/flow/MultiSwitch.java +++ b/src/java/org/apache/fop/fo/flow/MultiSwitch.java @@ -19,7 +19,6 @@ package org.apache.fop.fo.flow; -// XML import org.xml.sax.Locator; import org.apache.fop.apps.FOPException; @@ -39,7 +38,10 @@ public class MultiSwitch extends FObj { // private CommonAccessibility commonAccessibility; // End of property values - private static boolean notImplementedWarningGiven = false; + private static boolean notImplementedWarningGiven; + private FONode currentlyVisibleMultiCase; + private String autoToggle; + private String fittingStrategy; /** * Base constructor @@ -57,23 +59,44 @@ public class MultiSwitch extends FObj { } /** {@inheritDoc} */ + @Override public void bind(PropertyList pList) throws FOPException { super.bind(pList); + autoToggle = pList.get(PR_X_AUTO_TOGGLE).getString(); + fittingStrategy = pList.get(PR_X_FITTING_STRATEGY).getString(); // autoRestore = pList.get(PR_AUTO_RESTORE); } - /** {@inheritDoc} */ + @Override public void endOfNode() throws FOPException { if (firstChild == null) { missingChildElementError("(multi-case+)"); } + super.endOfNode(); + } + + @Override + public void finalizeNode() throws FOPException { + if (autoToggle.equals("best-fit")) { + // Nothing to do in this case + setCurrentlyVisibleNode(null); + } else { + FONodeIterator nodeIter = getChildNodes(); + while (nodeIter.hasNext()) { + MultiCase multiCase = (MultiCase) nodeIter.next(); + if (multiCase.isActuated()) { + multiCase.getHandler().filter(this); + } + } + } } /** * {@inheritDoc} * <br>XSL Content Model: (multi-case+) */ + @Override protected void validateChildNode(Locator loc, String nsURI, String localName) throws ValidationException { if (FO_URI.equals(nsURI)) { @@ -84,6 +107,7 @@ public class MultiSwitch extends FObj { } /** {@inheritDoc} */ + @Override public String getLocalName() { return "multi-switch"; } @@ -92,7 +116,25 @@ public class MultiSwitch extends FObj { * {@inheritDoc} * @return {@link org.apache.fop.fo.Constants#FO_MULTI_SWITCH} */ + @Override public int getNameId() { return FO_MULTI_SWITCH; } + + public void setCurrentlyVisibleNode(FONode node) { + currentlyVisibleMultiCase = node; + } + + public FONode getCurrentlyVisibleNode() { + return currentlyVisibleMultiCase; + } + + public String getFittingStrategy() { + return fittingStrategy; + } + + public String getAutoToggle() { + return autoToggle; + } + } diff --git a/src/java/org/apache/fop/fo/flow/MultiToggle.java b/src/java/org/apache/fop/fo/flow/MultiToggle.java index 10766680e..04da3083a 100644 --- a/src/java/org/apache/fop/fo/flow/MultiToggle.java +++ b/src/java/org/apache/fop/fo/flow/MultiToggle.java @@ -19,7 +19,6 @@ package org.apache.fop.fo.flow; -// FOP import org.xml.sax.Locator; import org.apache.fop.apps.FOPException; @@ -27,16 +26,17 @@ import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; +import org.apache.fop.fo.properties.StringProperty; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_multi-toggle"> * <code>fo:multi-toggle<code></a> property. */ -public class MultiToggle extends FObj { +public class MultiToggle extends FObj implements MultiCaseHandler { // The value of properties relevant for fo:multi-toggle (commented out for performance). // private CommonAccessibility commonAccessibility; - // public ToBeImplementedProperty prSwitchTo; + public StringProperty prSwitchTo; // End of property values private static boolean notImplementedWarningGiven = false; @@ -57,15 +57,17 @@ public class MultiToggle extends FObj { } /** {@inheritDoc} */ + @Override public void bind(PropertyList pList) throws FOPException { - // prSwitchTo = pList.get(PR_SWITCH_TO); - + super.bind(pList); + prSwitchTo = (StringProperty) pList.get(PR_SWITCH_TO); } /** * {@inheritDoc} * <br>XSL Content Model: (#PCDATA|%inline;|%block;)* */ + @Override protected void validateChildNode(Locator loc, String nsURI, String localName) throws ValidationException { if (FO_URI.equals(nsURI)) { @@ -76,6 +78,7 @@ public class MultiToggle extends FObj { } /** {@inheritDoc} */ + @Override public String getLocalName() { return "multi-toggle"; } @@ -84,7 +87,44 @@ public class MultiToggle extends FObj { * {@inheritDoc} * @return {@link org.apache.fop.fo.Constants#FO_MULTI_TOGGLE} */ + @Override public int getNameId() { return FO_MULTI_TOGGLE; } + + public void filter(MultiSwitch multiSwitch) throws FOPException { + if (multiSwitch.getCurrentlyVisibleNode() == null) { + multiSwitch.setCurrentlyVisibleNode(parent); + } + + FONode currentlyVisibleMultiCase = multiSwitch.getCurrentlyVisibleNode(); + + if (prSwitchTo.getString().equals("xsl-any")) { +// NoOp + } else if (prSwitchTo.getString().equals("xsl-preceding")) { + FONodeIterator nodeIter = multiSwitch.getChildNodes(currentlyVisibleMultiCase); + if (nodeIter != null) { + if (!nodeIter.hasPrevious()) { + currentlyVisibleMultiCase = nodeIter.lastNode(); + } else { + currentlyVisibleMultiCase = nodeIter.previousNode(); + } + } + } else if (prSwitchTo.getString().equals("xsl-following")) { + FONodeIterator nodeIter = multiSwitch.getChildNodes(currentlyVisibleMultiCase); + if (nodeIter != null) { + //Ignore the first node + nodeIter.next(); + if (!nodeIter.hasNext()) { + currentlyVisibleMultiCase = nodeIter.firstNode(); + } else { + currentlyVisibleMultiCase = nodeIter.nextNode(); + } + } + } else { + // Pick an fo:multi-case that matches a case-name... + } + + multiSwitch.setCurrentlyVisibleNode(currentlyVisibleMultiCase); + } } diff --git a/src/java/org/apache/fop/layoutmgr/Alternative.java b/src/java/org/apache/fop/layoutmgr/Alternative.java index 9162a63b5..9487b7462 100644 --- a/src/java/org/apache/fop/layoutmgr/Alternative.java +++ b/src/java/org/apache/fop/layoutmgr/Alternative.java @@ -19,24 +19,27 @@ package org.apache.fop.layoutmgr; +import java.util.LinkedList; import java.util.List; +/** + * An alternative has a set of fitness traits (e.g. occupied bpd and ipd, + * adjustment ratio, remaining size, etc.) that determine how good its + * eligibility is when evaluated by a fitting strategy. + */ public class Alternative { - /** remaining BPD after inserting the alternative */ + /** Remaining BPD after inserting the alternative. */ private int remainingBPD; - /** width of the alternative in block-progressing-dimension */ - private final int width; - /** Knuth element list */ + /** Size of the alternative in block-progression-direction. */ + private final int length; private final List<ListElement> knuthList; - /** Indicates whether we should consider the alternative or not */ - private boolean enabled; + private boolean enabled = true; - public Alternative(List<ListElement> knuthList, int width) { + public Alternative(List<ListElement> knuthList, int length) { this.knuthList = knuthList; - this.width = width; + this.length = length; this.remainingBPD = 0; - this.enabled = false; } public List<ListElement> getKnuthList() { @@ -47,8 +50,8 @@ public class Alternative { return remainingBPD; } - public int getWidth() { - return width; + public int getLength() { + return length; } public void setRemainingBPD(int remainingBPD) { @@ -68,20 +71,22 @@ public class Alternative { FIRST_FIT("first-fit") { @Override - public Alternative filter(List<Alternative> alternatives) { + public List<Alternative> filter(List<Alternative> alternatives) { + List<Alternative> alts = new LinkedList<Alternative>(); for (Alternative alt : alternatives) { if (alt.isEnabled()) { - return alt; + alts.add(alt); + break; } } - return null; + return alts; } }, SMALLEST_FIT("smallest-fit") { @Override - public Alternative filter(List<Alternative> alternatives) { + public List<Alternative> filter(List<Alternative> alternatives) { int biggestDiff = -Integer.MAX_VALUE; Alternative bestAlt = null; @@ -91,14 +96,16 @@ public class Alternative { bestAlt = alt; } } - return bestAlt; + List<Alternative> alts = new LinkedList<Alternative>(); + alts.add(bestAlt); + return alts; } }, BIGGEST_FIT("biggest-fit") { @Override - public Alternative filter(List<Alternative> alternatives) { + public List<Alternative> filter(List<Alternative> alternatives) { int smallestDiff = Integer.MAX_VALUE; Alternative bestAlt = null; @@ -108,7 +115,26 @@ public class Alternative { bestAlt = alt; } } - return bestAlt; + List<Alternative> alts = new LinkedList<Alternative>(); + alts.add(bestAlt); + return alts; + } + }, + + ANY("any") { + + @Override + public List<Alternative> filter(List<Alternative> alternatives) { + List<Alternative> alts = new LinkedList<Alternative>(); + + int remainingSpace = Integer.MAX_VALUE; + for (Alternative alt : alternatives) { + if (alt.isEnabled() && alt.getLength() <= remainingSpace) { + alts.add(alt); + remainingSpace = alt.getRemainingBPD(); + } + } + return alts; } }; @@ -122,11 +148,20 @@ public class Alternative { return strategyName; } + public static FittingStrategy make(String strategyName) { + for (FittingStrategy fs : FittingStrategy.values()) { + if (fs.getStrategyName().equals(strategyName)) { + return fs; + } + } + return null; + } + /** * @param alternatives the list of potential candidate {@link Alternative} * @return the best alternative according to the strategy being employed */ - public abstract Alternative filter(List<Alternative> alternatives); + public abstract List<Alternative> filter(List<Alternative> alternatives); } } diff --git a/src/java/org/apache/fop/layoutmgr/AlternativeBlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AlternativeBlockLayoutManager.java deleted file mode 100644 index e82575a4b..000000000 --- a/src/java/org/apache/fop/layoutmgr/AlternativeBlockLayoutManager.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * 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} */ - @Override - public List getNextKnuthElements(LayoutContext context, int alignment, - Stack lmStack, Position positionAtIPDChange, - LayoutManager restartAtLM) { - LayoutManager curLM; - List<ListElement> knuthList = new LinkedList<ListElement>(); - while ((curLM = getChildLM()) != null) { - LayoutContext childLC = makeChildLayoutContext(context); - - List childKnuthList = null; - if (!curLM.isFinished()) { - childKnuthList = curLM.getNextKnuthElements(childLC, alignment); - } - if (childKnuthList != null) { - wrapPositionElements(childKnuthList, knuthList); - } - } - setFinished(true); - return knuthList; - } - - /** - * "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} */ - @Override - public boolean isRestartable() { - return true; - } - -} diff --git a/src/java/org/apache/fop/layoutmgr/BestFitLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BestFitLayoutManager.java deleted file mode 100644 index 9108663f9..000000000 --- a/src/java/org/apache/fop/layoutmgr/BestFitLayoutManager.java +++ /dev/null @@ -1,280 +0,0 @@ -/* - * 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.Alternative.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 BestFitPenalty bestFitPenalty; - - private static class BestFitPosition extends Position { - - public BestFitPosition(LayoutManager lm) { - super(lm); - } - - } - - private final 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} */ - @Override - public List getNextKnuthElements(LayoutContext context, int alignment, - Stack lmStack, Position positionAtIPDChange, - LayoutManager restartAtLM) { - LayoutManager curLM; // currently active LM - bestFitPenalty = new BestFitPenalty(fittingStrategy, new BestFitPosition(this)); - while ((curLM = getChildLM()) != null) { - LayoutContext childLC = makeChildLayoutContext(context); - - List childKnuthList = null; - if (!curLM.isFinished()) { - childKnuthList = curLM.getNextKnuthElements(childLC, alignment); - } - if (childKnuthList != null) { - List<ListElement> newKnuthList = new LinkedList<ListElement>(); - this.wrapPositionElements(childKnuthList, newKnuthList); - int contentLength = ElementListUtils.calcFullContentLength(newKnuthList); - // Add a new variant - bestFitPenalty.addAlternative(new Alternative(newKnuthList, contentLength)); - } - } - setFinished(true); - List<ListElement> finalList = new LinkedList<ListElement>(); - // Enclose the penalty with two empty Knuth boxes - // to prevent the layout engine from ignoring it. - // I know this seems like a hack, but I'll address - // this issue properly when more tests are done - finalList.add(new KnuthBox(0, new Position(this), false)); - finalList.add(bestFitPenalty); - finalList.add(new KnuthBox(0, new Position(this), false)); - 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; - 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 (bestFitPenalty.getBestAlternative() != null) { - Alternative bestAlt = bestFitPenalty.getBestAlternative(); - 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} */ - @Override - public boolean isRestartable() { - return true; - } - -} diff --git a/src/java/org/apache/fop/layoutmgr/BestFitLayoutUtils.java b/src/java/org/apache/fop/layoutmgr/BestFitLayoutUtils.java new file mode 100644 index 000000000..af82df386 --- /dev/null +++ b/src/java/org/apache/fop/layoutmgr/BestFitLayoutUtils.java @@ -0,0 +1,117 @@ +/* + * 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. + */ + +package org.apache.fop.layoutmgr; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.apache.fop.layoutmgr.Alternative.FittingStrategy; + +/* + * Utility class used in {@link MultiSwitchLayoutManager} + * to handle the <i>best-fit</i> property value if specified in {@link MultiSwitch} + */ +public final class BestFitLayoutUtils { + + private BestFitLayoutUtils() { } + + static class BestFitPosition extends Position { + + public List<ListElement> knuthList; + + public BestFitPosition(LayoutManager lm) { + super(lm); + } + + public List<Position> getPositionList() { + List<Position> positions = new LinkedList<Position>(); + if (knuthList != null) { + + SpaceResolver.performConditionalsNotification(knuthList, 0, knuthList.size() - 1, -1); + + for (ListElement elem : knuthList) { + if (elem.getPosition() != null && elem.getLayoutManager() != null) { + positions.add(elem.getPosition()); + } + } + } + return positions; + } + + public void setKnuthList(List<ListElement> knuthList) { + this.knuthList = knuthList; + } + + public List<ListElement> getKnuthList() { + return knuthList; + } + + } + + public static Alternative makeAlternative(List<ListElement> childList) { + // Add a zero penalty to make the SpaceResolver + // transform Space elements into Knuth glues. + childList.add(KnuthPenalty.DUMMY_ZERO_PENALTY); + SpaceResolver.resolveElementList(childList); + int contentLength = ElementListUtils.calcContentLength(childList); + return new Alternative(childList, contentLength); + } + + public static List<ListElement> getKnuthList(LayoutManager lm, + List<List<ListElement>> childrenLists, + FittingStrategy strategy) { + List<ListElement> knuthList = new LinkedList<ListElement>(); + Iterator<List<ListElement>> iter = childrenLists.iterator(); + BestFitPenalty bestFitPenalty = new BestFitPenalty(strategy, new BestFitPosition(lm)); + while (iter.hasNext()) { + List<ListElement> childList = iter.next(); + bestFitPenalty.addAlternative(makeAlternative(childList)); + } + // A penalty must always be preceded by a box + // to be considered as a valid breakpoint. + addKnuthPenalty(lm, knuthList, bestFitPenalty); + return knuthList; + } + + public static void addKnuthPenalty(LayoutManager lm, List<ListElement> list, + KnuthPenalty bestFitPenalty) { + + list.add(0, new KnuthBox(0, new Position(lm), false)); + list.add(bestFitPenalty); + list.add(new KnuthBox(0, new Position(lm), false)); + } + + public static List<Position> getPositionList(LayoutManager lm, PositionIterator posIter) { + + // "unwrap" the NonLeafPositions stored in parentIter + // and put them in a new list; + LinkedList<Position> positionList = new LinkedList<Position>(); + Position pos; + while (posIter.hasNext()) { + pos = posIter.next(); + if (pos instanceof BestFitPosition) { + positionList.addAll(((BestFitPosition) pos).getPositionList()); + } else { + positionList.add(pos); + } + } + return positionList; + } + +} diff --git a/src/java/org/apache/fop/layoutmgr/BestFitPenalty.java b/src/java/org/apache/fop/layoutmgr/BestFitPenalty.java index badd3dbf0..d3ee6f722 100644 --- a/src/java/org/apache/fop/layoutmgr/BestFitPenalty.java +++ b/src/java/org/apache/fop/layoutmgr/BestFitPenalty.java @@ -23,17 +23,20 @@ import java.util.LinkedList; import java.util.List; import org.apache.fop.layoutmgr.Alternative.FittingStrategy; +import org.apache.fop.layoutmgr.BestFitLayoutUtils.BestFitPosition; /** - * A dummy penalty used in {@link BestFitLayoutManager} to store - * the different alternatives in {@link fox:best-fit} + * A penalty class used to specify a set of alternatives for the layout engine + * to choose from. The chosen alternative must have an occupied size + * that is less than the available BPD of the current page + * and it must also be the best match when it is evaluated by {@link FittingStrategy}. */ - public class BestFitPenalty extends KnuthPenalty { private final LinkedList<Alternative> alternatives; private final FittingStrategy strategy; - private Alternative bestAlternative = null; + public boolean canFit = true; + private int currentAltIndex; public BestFitPenalty(FittingStrategy strategy, Position pos) { super(0, 0, false, pos, false); @@ -49,30 +52,59 @@ public class BestFitPenalty extends KnuthPenalty { return alternatives; } - public FittingStrategy getStrategyType() { + public FittingStrategy getFittingStrategy() { return strategy; } - public Alternative getBestAlternative() { - if (bestAlternative != null) { - return bestAlternative; + @Override + public int getWidth() { + if (currentAltIndex == -1) { + return 0; + } + return alternatives.get(currentAltIndex).getLength(); + } + + public boolean hasMoreAlternatives() { + return currentAltIndex != -1; + } + + public void considerNextAlternative() { + if (currentAltIndex < alternatives.size() - 1) { + currentAltIndex++; } else { - bestAlternative = strategy.filter(alternatives); - return bestAlternative; + currentAltIndex = -1; } } -// public void setBestAlternative(Alternative bestAlternative) { -// this.bestAlternative = bestAlternative; -// } + @Override + public Position getPosition() { + if (currentAltIndex != -1) { + Position pos = super.getPosition(); + if (alternatives.size() > 0) { + getBestFitPosition().setKnuthList(alternatives.get(currentAltIndex).getKnuthList()); + } + return pos; + } + return null; + } + + public BestFitPosition getBestFitPosition() { + Position pos = super.getPosition(); + while (pos != null) { + if (pos instanceof BestFitPosition) { + return (BestFitPosition) pos; + } + pos = pos.getPosition(); + } + return null; + } - /** {@inheritDoc} */ @Override public String toString() { String str = super.toString(); StringBuffer buffer = new StringBuffer(64); buffer.append(" number of alternatives = " + getAlternatives().size()); - buffer.append(" fitting-strategy = " + strategy); + buffer.append(" fitting-strategy = " + strategy.getStrategyName()); 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 71106f516..4cf15f3a0 100644 --- a/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java +++ b/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java @@ -304,7 +304,6 @@ public abstract class BreakingAlgorithm { } /** {@inheritDoc} */ - @Override public String toString() { return "<KnuthNode at " + position + " " + totalWidth + "+" + totalStretch + "-" + totalShrink @@ -931,13 +930,7 @@ public abstract class BreakingAlgorithm { if (node.position == elementIdx) { continue; } - int difference; - if (element instanceof BestFitPenalty) { - BestFitPenalty penalty = (BestFitPenalty)element; - difference = handleBestFitPenalty(penalty, node, elementIdx); - } else { - difference = computeDifference(node, element, elementIdx); - } + int difference = computeDifference(node, element, elementIdx); if (!elementCanEndLine(element, endLine, difference)) { log.trace("Skipping legal break"); break; @@ -976,33 +969,6 @@ public abstract class BreakingAlgorithm { } } - private int handleBestFitPenalty(BestFitPenalty penalty, KnuthNode node, - int elementIdx) { - // Find the alternatives that can be fitted inside the remaining space - for (int i = 0; i < penalty.getAlternatives().size(); ++i) { - Alternative alt = penalty.getAlternatives().get(i); - int difference = computeDifference(node, new KnuthPenalty(alt.getWidth(), - 0, false, null, false), elementIdx); - double r = computeAdjustmentRatio(node, difference); - //if (r >= -1) { - alt.setRemainingBPD(difference); - alt.setEnabled(r >= -1); - //altManager.addAlternative(alt); - //} - } - Alternative bestAlt = penalty.getBestAlternative(); - if (bestAlt != null) { - //penalty.setBestAlternative(bestAlt); - // Add an empty KnuthBox to represent the chosen alternative - par.add(elementIdx + 1, new KnuthBox(bestAlt.getWidth(), null, false)); - return bestAlt.getRemainingBPD(); - } else { - // No "good" alternative was found - return computeDifference(node, new KnuthPenalty(0, - 0, false, null, false), elementIdx); - } - } - /** * Check if the given {@link KnuthElement} can end the line with the given * number. diff --git a/src/java/org/apache/fop/layoutmgr/ElementListUtils.java b/src/java/org/apache/fop/layoutmgr/ElementListUtils.java index 560747bcc..e6be4ca63 100644 --- a/src/java/org/apache/fop/layoutmgr/ElementListUtils.java +++ b/src/java/org/apache/fop/layoutmgr/ElementListUtils.java @@ -163,37 +163,6 @@ 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)) { - // 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 diff --git a/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java b/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java index 2d08e674c..cf8b74cc0 100644 --- a/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java +++ b/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java @@ -34,8 +34,6 @@ 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; @@ -51,6 +49,8 @@ import org.apache.fop.fo.flow.InstreamForeignObject; import org.apache.fop.fo.flow.Leader; import org.apache.fop.fo.flow.ListBlock; import org.apache.fop.fo.flow.ListItem; +import org.apache.fop.fo.flow.MultiCase; +import org.apache.fop.fo.flow.MultiSwitch; import org.apache.fop.fo.flow.PageNumber; import org.apache.fop.fo.flow.PageNumberCitation; import org.apache.fop.fo.flow.PageNumberCitationLast; @@ -98,7 +98,7 @@ public class LayoutManagerMapping implements LayoutManagerMaker { private static final Log LOG = LogFactory.getLog(LayoutManagerMapping.class); /** The map of LayoutManagerMakers */ - private Map makers = new HashMap(); + private final Map makers = new HashMap(); /** default constructor */ public LayoutManagerMapping() { @@ -144,8 +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()); + registerMaker(MultiCase.class, new MultiCaseLayoutManagerMaker()); + registerMaker(MultiSwitch.class, new MultiSwitchLayoutManagerMaker()); } /** @@ -449,19 +449,30 @@ public class LayoutManagerMapping implements LayoutManagerMaker { } } - /** a layout manager maker */ - public class BestFitLayoutManagerMaker extends Maker { - /** {@inheritDoc} */ + public class MultiSwitchLayoutManagerMaker extends Maker { + + @Override public void make(FONode node, List lms) { - lms.add(new BestFitLayoutManager((BestFit) node)); + MultiSwitch multiSwitch = (MultiSwitch) node; + MultiSwitchLayoutManager mslm = new MultiSwitchLayoutManager(multiSwitch); + FONode multiCase = multiSwitch.getCurrentlyVisibleNode(); + if (multiCase != null) { + FONodeIterator childIter = multiCase.getChildNodes(); + while (childIter.hasNext()) { + FONode child = (FONode) childIter.next(); + makeLayoutManagers(child, lms); + } + } else { + lms.add(mslm); + } } } - /** a layout manager maker */ - public class AlternativeBlockLayoutManagerMaker extends Maker { - /** {@inheritDoc} */ + public class MultiCaseLayoutManagerMaker extends Maker { + + @Override public void make(FONode node, List lms) { - lms.add(new AlternativeBlockLayoutManager((AlternativeBlock) node)); + lms.add(new MultiCaseLayoutManager((MultiCase) node)); } } diff --git a/src/java/org/apache/fop/layoutmgr/MultiCaseLayoutManager.java b/src/java/org/apache/fop/layoutmgr/MultiCaseLayoutManager.java new file mode 100644 index 000000000..bc3e61b8e --- /dev/null +++ b/src/java/org/apache/fop/layoutmgr/MultiCaseLayoutManager.java @@ -0,0 +1,89 @@ +/* + * 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. + */ + +package org.apache.fop.layoutmgr; + +import org.apache.fop.area.Area; +import org.apache.fop.area.Block; +import org.apache.fop.area.LineArea; +import org.apache.fop.fo.FObj; + +public class MultiCaseLayoutManager extends BlockStackingLayoutManager { + + private Block curBlockArea; + + public MultiCaseLayoutManager(FObj node) { + super(node); + } + + @Override + public Keep getKeepTogether() { + return Keep.KEEP_AUTO; + } + + @Override + public Keep getKeepWithNext() { + return Keep.KEEP_AUTO; + } + + @Override + public Keep getKeepWithPrevious() { + return Keep.KEEP_AUTO; + } + + @Override + public Area getParentArea(Area childArea) { + if (curBlockArea == null) { + curBlockArea = new Block(); + curBlockArea.setIPD(super.getContentAreaIPD()); + // Set up dimensions + // Must get dimensions from parent area + /*Area parentArea = */parentLayoutManager.getParentArea(curBlockArea); + setCurrentArea(curBlockArea); + } + return curBlockArea; + } + + @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. + */ + @Override + protected void flush() { + if (getCurrentArea() != null) { + super.flush(); + } + } + + /** {@inheritDoc} */ + @Override + public void addAreas(PositionIterator posIter, LayoutContext context) { + AreaAdditionUtil.addAreas(this, posIter, context); + flush(); + } + +} diff --git a/src/java/org/apache/fop/layoutmgr/MultiSwitchLayoutManager.java b/src/java/org/apache/fop/layoutmgr/MultiSwitchLayoutManager.java new file mode 100644 index 000000000..6f628d1f5 --- /dev/null +++ b/src/java/org/apache/fop/layoutmgr/MultiSwitchLayoutManager.java @@ -0,0 +1,157 @@ +/* + * 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. + */ + +package org.apache.fop.layoutmgr; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import org.apache.fop.area.Area; +import org.apache.fop.area.Block; +import org.apache.fop.area.LineArea; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.FONode.FONodeIterator; +import org.apache.fop.fo.FObj; +import org.apache.fop.fo.flow.MultiSwitch; +import org.apache.fop.layoutmgr.Alternative.FittingStrategy; + +public class MultiSwitchLayoutManager extends BlockStackingLayoutManager { + + private Block curBlockArea; + + public MultiSwitchLayoutManager(FObj node) { + super(node); + } + + @Override + public List<ListElement> getNextKnuthElements(LayoutContext context, int alignment) { + + referenceIPD = context.getRefIPD(); + List<List<ListElement>> childrenLists = new LinkedList<List<ListElement>>(); + LayoutManager childLM; + while ((childLM = getChildLM()) != null) { + if (!childLM.isFinished()) { + LayoutContext childLC = makeChildLayoutContext(context); + List childElements = childLM.getNextKnuthElements(childLC, alignment); + if (childElements != null) { + List<ListElement> newList = new LinkedList<ListElement>(); + wrapPositionElements(childElements, newList); + childrenLists.add(newList); + } + } + } + setFinished(true); + return BestFitLayoutUtils.getKnuthList(this, childrenLists, FittingStrategy.FIRST_FIT); + } + + @Override + protected List<LayoutManager> createChildLMs(int size) { + MultiSwitch multiSwitch = (MultiSwitch) getFObj(); + if (multiSwitch.getCurrentlyVisibleNode() != null) { + List<LayoutManager> newLMs = new ArrayList<LayoutManager>(size); + if (childLMs.size() == 0) { + createMultiCaseLM(multiSwitch.getCurrentlyVisibleNode()); + return new ArrayList<LayoutManager>(size); + } + return newLMs; + } else { + return super.createChildLMs(size); + } + } + + private void createMultiCaseLM(FONode multiCase) { + FONodeIterator childIter = multiCase.getChildNodes(); + while (childIter.hasNext()) { + List<LayoutManager> newLMs = new ArrayList<LayoutManager>(1); + getPSLM().getLayoutManagerMaker() + .makeLayoutManagers((childIter.nextNode()), newLMs); + if (!newLMs.isEmpty()) { + this.getParent().addChildLM(newLMs.get(0)); + } + } + } + + @Override + public Keep getKeepTogether() { + return Keep.KEEP_AUTO; + } + + @Override + public Keep getKeepWithNext() { + return Keep.KEEP_AUTO; + } + + @Override + public Keep getKeepWithPrevious() { + return Keep.KEEP_AUTO; + } + + @Override + public int getContentAreaIPD() { + if (curBlockArea != null) { + return curBlockArea.getIPD(); + } + return super.getContentAreaIPD(); + } + + @Override + public Area getParentArea(Area childArea) { + if (curBlockArea == null) { + curBlockArea = new Block(); + curBlockArea.setIPD(super.getContentAreaIPD()); + setCurrentArea(curBlockArea); + // Set up dimensions + // Must get dimensions from parent area + /*Area parentArea = */parentLayoutManager.getParentArea(curBlockArea); + } + return curBlockArea; + } + + @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. + */ + @Override + protected void flush() { + if (getCurrentArea() != null) { + parentLayoutManager.addChildArea(getCurrentArea()); + } + } + + @Override + public void addAreas(PositionIterator posIter, LayoutContext context) { + + List<Position> positionList = BestFitLayoutUtils.getPositionList(this, posIter); + PositionIterator newPosIter = new PositionIterator( + positionList.listIterator()); + + AreaAdditionUtil.addAreas(this, newPosIter, context); + flush(); + } + +} diff --git a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java index 7183de45a..d441b37bd 100644 --- a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java +++ b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java @@ -149,6 +149,9 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { /** Index of the last inserted element of the last inserted footnote. */ public int footnoteElementIndex; + public boolean bestFitNode = false; + public boolean skipNode = false; + public KnuthPageNode(int position, int line, int fitness, int totalWidth, int totalStretch, int totalShrink, @@ -162,6 +165,12 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { this.totalFootnotes = totalFootnotes; this.footnoteListIndex = footnoteListIndex; this.footnoteElementIndex = footnoteElementIndex; + if (getElement(position) instanceof BestFitPenalty) { + if (log.isDebugEnabled()) { + log.debug("Creating a KnuthPageNode for a BestFitPenalty."); + } + bestFitNode = true; + } } } @@ -394,6 +403,25 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { /** {@inheritDoc} */ @Override protected int restartFrom(KnuthNode restartingNode, int currentIndex) { + if (getLastTooLong() != null) { + KnuthPageNode lastTooLong = (KnuthPageNode) getLastTooLong(); + if (lastTooLong.skipNode) { + if (log.isDebugEnabled()) { + log.debug("Alternative does not fit in the current page. " + + "Switching to the next one in the list"); + } + int penaltyIndex = currentIndex; + // In case the paragraph has changed... + if (lastTooLong.previous.previous == null) { + if (par.get(0) == KnuthPenalty.DUMMY_ZERO_PENALTY) { + ++penaltyIndex; + } + } + BestFitPenalty bestFitPenalty = (BestFitPenalty) getElement(penaltyIndex); + bestFitPenalty.considerNextAlternative(); + restartingNode = restartingNode.previous; + } + } int returnValue = super.restartFrom(restartingNode, currentIndex); newFootnotes = false; if (footnotesPending) { @@ -491,6 +519,22 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { } /** {@inheritDoc} */ + protected void forceNode(KnuthNode node, int line, int elementIdx, + int difference, double r, double demerits, int fitnessClass, + int availableShrink, int availableStretch) { + + super.forceNode(node, line, elementIdx, difference, r, demerits, + fitnessClass, availableShrink, availableStretch); + + KnuthPageNode lastTooLong = (KnuthPageNode) getLastTooLong(); + if (lastTooLong != null) { + if (lastTooLong.bestFitNode) { + lastTooLong.skipNode = true; + } + } + } + + /** {@inheritDoc} */ @Override protected int computeDifference(KnuthNode activeNode, KnuthElement element, int elementIndex) { |