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-ffa450edef68tags/fop-2_0
@@ -815,9 +815,12 @@ public interface Constants { | |||
/** Scope for table header */ | |||
int PR_X_HEADER_COLUMN = 290; | |||
/** Property constant - FOP proprietary*/ | |||
int PR_X_FITTING_STRATEGY = 291; | |||
/** Number of property constants defined */ | |||
int PROPERTY_COUNT = 290; | |||
int PROPERTY_COUNT = 291; | |||
// compound property constants | |||
@@ -2613,7 +2613,12 @@ public final class FOPropertyMapping implements Constants { | |||
m.setInherited(false); | |||
m.setDefault(""); | |||
addPropertyMaker("fox:alt-text", m); | |||
//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:border-*-radius-* | |||
m = new CondLengthProperty.Maker(PR_X_BORDER_BEFORE_RADIUS_START); |
@@ -0,0 +1,92 @@ | |||
/* | |||
* 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); | |||
} | |||
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) : "")); | |||
} | |||
} | |||
public void startOfNode() throws FOPException { | |||
if (log.isDebugEnabled()) | |||
log.debug("AlternativeBlock.startOfNode()"); | |||
} | |||
public void endOfNode() throws FOPException { | |||
if (log.isDebugEnabled()) | |||
log.debug("AlternativeBlock.endOfNode()"); | |||
} | |||
/** | |||
* Content model: see {@link Block} | |||
*/ | |||
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, FO_URI, localName); | |||
} | |||
} | |||
} | |||
@Override | |||
public String getLocalName() { | |||
return OBJECT_NAME; | |||
} | |||
@Override | |||
public String getNormalNamespacePrefix() { | |||
return ExtensionElementMapping.STANDARD_PREFIX; | |||
} | |||
public String getNamespaceURI() { | |||
return ExtensionElementMapping.URI; | |||
} | |||
} |
@@ -0,0 +1,116 @@ | |||
/* | |||
* 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; | |||
import org.apache.fop.layoutmgr.AlternativeManager.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 static final String OBJECT_NAME = "best-fit"; | |||
private FittingStrategy strategy = FittingStrategy.FIRST_FIT; | |||
public BestFit(FONode parent) { | |||
super(parent); | |||
} | |||
/** {@inheritDoc} */ | |||
public void bind(PropertyList pList) throws FOPException { | |||
super.bind(pList); | |||
String strategyName = pList.get(PR_X_FITTING_STRATEGY).getString(); | |||
if (strategyName.equals("first-fit")) | |||
strategy = FittingStrategy.FIRST_FIT; | |||
else if (strategyName.equals("smallest-fit")) | |||
strategy = FittingStrategy.SMALLEST_FIT; | |||
else if (strategyName.equals("biggest-fit")) | |||
strategy = FittingStrategy.BIGGEST_FIT; | |||
else { | |||
log.warn("Unrecognized strategy name => " + strategyName + ". Using default strategy (first-fit"); | |||
strategy = FittingStrategy.FIRST_FIT; | |||
} | |||
} | |||
public void processNode(String elementName, Locator locator, | |||
Attributes attlist, PropertyList pList) throws FOPException { | |||
if (log.isDebugEnabled()) { | |||
log.debug("org.apache.fop.fo.extensions.BestFit: " + elementName | |||
+ (locator != null ? " at " + getLocatorString(locator) : "")); | |||
} | |||
pList.addAttributesToList(attlist); | |||
bind(pList); | |||
} | |||
public void startOfNode() throws FOPException { | |||
if (log.isDebugEnabled()) | |||
log.debug("BestFit.startOfNode()"); | |||
} | |||
public void endOfNode() throws FOPException { | |||
if (log.isDebugEnabled()) | |||
log.debug("BestFit.endOfNode()"); | |||
} | |||
/** | |||
* {@inheritDoc} | |||
* Content model: (fox:alternative-block)+ | |||
*/ | |||
protected void validateChildNode(Locator loc, String nsURI, String localName) | |||
throws ValidationException { | |||
if (FO_URI.equals(nsURI)) { | |||
if (!"alternative-block".equals(localName)) { | |||
invalidChildError(loc, FO_URI, localName); | |||
} | |||
} | |||
} | |||
@Override | |||
public String getLocalName() { | |||
return OBJECT_NAME; | |||
} | |||
@Override | |||
public String getNormalNamespacePrefix() { | |||
return ExtensionElementMapping.STANDARD_PREFIX; | |||
} | |||
public String getNamespaceURI() { | |||
return ExtensionElementMapping.URI; | |||
} | |||
public FittingStrategy getStrategy() { | |||
return strategy; | |||
} | |||
} |
@@ -49,6 +49,7 @@ public class ExtensionElementMapping extends ElementMapping { | |||
PROPERTY_ATTRIBUTES.add("orphan-content-limit"); | |||
PROPERTY_ATTRIBUTES.add("internal-destination"); | |||
PROPERTY_ATTRIBUTES.add("disable-column-balancing"); | |||
PROPERTY_ATTRIBUTES.add("fitting-strategy"); | |||
//These are FOP's extension properties for accessibility | |||
PROPERTY_ATTRIBUTES.add("alt-text"); | |||
PROPERTY_ATTRIBUTES.add("header"); | |||
@@ -85,6 +86,8 @@ 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()); | |||
} | |||
} | |||
@@ -99,6 +102,18 @@ public class ExtensionElementMapping extends ElementMapping { | |||
return new ExternalDocument(parent); | |||
} | |||
} | |||
static class BestFitMaker extends ElementMapping.Maker { | |||
public FONode make(FONode parent) { | |||
return new BestFit(parent); | |||
} | |||
} | |||
static class AlternativeBlockMaker extends ElementMapping.Maker { | |||
public FONode make(FONode parent) { | |||
return new AlternativeBlock(parent); | |||
} | |||
} | |||
/** {@inheritDoc} */ | |||
public String getStandardPrefix() { |
@@ -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); |
@@ -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; | |||
} | |||
} |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -162,6 +162,37 @@ public final class ElementListUtils { | |||
return len; | |||
} | |||
/** | |||
* 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. | |||
@@ -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; |
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
} | |||
@@ -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)); | |||
} | |||
} | |||
} |
@@ -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; |
@@ -19,10 +19,16 @@ | |||
package org.apache.fop; | |||
import static org.junit.Assert.assertEquals; | |||
import java.util.List; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import static org.junit.Assert.assertEquals; | |||
import org.apache.fop.layoutmgr.AlternativeManager.Alternative; | |||
import org.apache.fop.layoutmgr.AlternativeManager.FittingStrategy; | |||
import org.apache.fop.layoutmgr.BestFitPenalty; | |||
import org.apache.fop.layoutmgr.BlockKnuthSequence; | |||
import org.apache.fop.layoutmgr.BreakingAlgorithm; | |||
import org.apache.fop.layoutmgr.ElementListObserver; | |||
@@ -30,8 +36,6 @@ import org.apache.fop.layoutmgr.KnuthBox; | |||
import org.apache.fop.layoutmgr.KnuthGlue; | |||
import org.apache.fop.layoutmgr.KnuthPenalty; | |||
import org.apache.fop.layoutmgr.KnuthSequence; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
/** | |||
* Tests the Knuth algorithm implementation. | |||
@@ -63,6 +67,27 @@ public class KnuthAlgorithmTestCase { | |||
return seq; | |||
} | |||
private static class BestFitPenaltyTestCase { | |||
private static KnuthSequence getKnuthSequence() { | |||
KnuthSequence seq = new BlockKnuthSequence(); | |||
FittingStrategy strategies[] = {FittingStrategy.FIRST_FIT, | |||
FittingStrategy.SMALLEST_FIT, | |||
FittingStrategy.BIGGEST_FIT}; | |||
for (int i = 0; i < 3; ++i) { | |||
BestFitPenalty bestFitPenalty = new BestFitPenalty(strategies[i], null); | |||
bestFitPenalty.addAlternative(new Alternative(null, 25000)); | |||
bestFitPenalty.addAlternative(new Alternative(null, 5000)); | |||
bestFitPenalty.addAlternative(new Alternative(null, 29000)); | |||
seq.add(new KnuthBox(0, null, false)); | |||
seq.add(bestFitPenalty); | |||
seq.add(new KnuthPenalty(0, -KnuthPenalty.INFINITE, false, null, false)); | |||
} | |||
return seq; | |||
} | |||
} | |||
/** | |||
* Tests a special condition where a negative-length glue occurs directly after a break | |||
* possibility. | |||
@@ -79,6 +104,22 @@ public class KnuthAlgorithmTestCase { | |||
assertEquals(5000, parts[0].difference); | |||
assertEquals(5000, parts[1].difference); | |||
} | |||
/** | |||
* Testcase for BestFitPenalty | |||
* @throws Exception if an error occurs | |||
*/ | |||
@Test | |||
public void test2() throws Exception { | |||
MyBreakingAlgorithm algo = new MyBreakingAlgorithm(0, 0, true, true, 0); | |||
algo.setConstantLineWidth(30000); | |||
KnuthSequence seq = BestFitPenaltyTestCase.getKnuthSequence(); | |||
algo.findBreakingPoints(seq, 1, true, BreakingAlgorithm.ALL_BREAKS); | |||
Part[] parts = algo.getParts(); | |||
assertEquals("Sequence must produce 3 parts", 3, parts.length); | |||
assertEquals(5000, parts[0].difference); | |||
assertEquals(25000, parts[1].difference); | |||
assertEquals(1000, parts[2].difference); | |||
} | |||
private class Part { | |||
private int difference; | |||
@@ -88,7 +129,7 @@ public class KnuthAlgorithmTestCase { | |||
private class MyBreakingAlgorithm extends BreakingAlgorithm { | |||
private List parts = new java.util.ArrayList(); | |||
private List<Part> parts = new java.util.ArrayList<Part>(); | |||
public MyBreakingAlgorithm(int align, int alignLast, boolean first, | |||
boolean partOverflowRecovery, int maxFlagCount) { | |||
@@ -96,7 +137,7 @@ public class KnuthAlgorithmTestCase { | |||
} | |||
public Part[] getParts() { | |||
return (Part[])parts.toArray(new Part[parts.size()]); | |||
return parts.toArray(new Part[parts.size()]); | |||
} | |||
public void updateData1(int total, double demerits) { |