git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@198637 13f79535-47bb-0310-9956-ffa450edef68tags/fop-0_90-alpha1
@@ -18,6 +18,9 @@ | |||
package org.apache.fop.area; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
// may combine with before float into a conditional area | |||
/** | |||
@@ -53,5 +56,24 @@ public class Footnote extends BlockParent { | |||
return separator; | |||
} | |||
public void setTop(int top) { | |||
this.top = top; | |||
} | |||
public int getTop() { | |||
return top; | |||
} | |||
public void addBlock(Block child) { | |||
if (children == null) { | |||
children = new ArrayList(); | |||
} | |||
this.setBPD(this.getBPD() + child.getBPD()); | |||
children.add(child); | |||
} | |||
public List getChildAreas() { | |||
return children; | |||
} | |||
} | |||
@@ -38,7 +38,7 @@ public class Footnote extends FObj { | |||
private CommonAccessibility commonAccessibility; | |||
// End of property values | |||
private Inline inlineFO = null; | |||
private Inline footnoteCitation = null; | |||
private FootnoteBody footnoteBody; | |||
/** | |||
@@ -69,7 +69,7 @@ public class Footnote extends FObj { | |||
*/ | |||
protected void endOfNode() throws FOPException { | |||
super.endOfNode(); | |||
if (inlineFO == null || footnoteBody == null) { | |||
if (footnoteCitation == null || footnoteBody == null) { | |||
missingChildElementError("(inline,footnote-body)"); | |||
} | |||
getFOEventHandler().endFootnote(this); | |||
@@ -87,11 +87,11 @@ public class Footnote extends FObj { | |||
protected void validateChildNode(Locator loc, String nsURI, String localName) | |||
throws ValidationException { | |||
if (nsURI == FO_URI && localName.equals("inline")) { | |||
if (inlineFO != null) { | |||
if (footnoteCitation != null) { | |||
tooManyNodesError(loc, "fo:inline"); | |||
} | |||
} else if (nsURI == FO_URI && localName.equals("footnote-body")) { | |||
if (inlineFO == null) { | |||
if (footnoteCitation == null) { | |||
nodesOutOfOrderError(loc, "fo:inline", "fo:footnote-body"); | |||
} else if (footnoteBody != null) { | |||
tooManyNodesError(loc, "fo:footnote-body"); | |||
@@ -106,7 +106,7 @@ public class Footnote extends FObj { | |||
*/ | |||
public void addChildNode(FONode child) { | |||
if (((FObj)child).getNameId() == FO_INLINE) { | |||
inlineFO = (Inline) child; | |||
footnoteCitation = (Inline) child; | |||
} else if (((FObj)child).getNameId() == FO_FOOTNOTE_BODY) { | |||
footnoteBody = (FootnoteBody) child; | |||
} | |||
@@ -114,10 +114,18 @@ public class Footnote extends FObj { | |||
/** | |||
* Public accessor for inline FO | |||
* @return the Inline object stored as inline FO | |||
* @return the Inline child | |||
*/ | |||
public Inline getInlineFO() { | |||
return inlineFO; | |||
public Inline getFootnoteCitation() { | |||
return footnoteCitation; | |||
} | |||
/** | |||
* Public accessor for footnote-body FO | |||
* @return the FootnoteBody child | |||
*/ | |||
public FootnoteBody getFootnoteBody() { | |||
return footnoteBody; | |||
} | |||
/** |
@@ -35,16 +35,24 @@ public abstract class AbstractBreaker { | |||
/** logging instance */ | |||
protected static Log log = LogFactory.getLog(AbstractBreaker.class); | |||
/*LF*/ | |||
public static class PageBreakPosition extends LeafPosition { | |||
double bpdAdjust; // Percentage to adjust (stretch or shrink) | |||
int difference; | |||
int footnoteFirstListIndex; | |||
int footnoteFirstElementIndex; | |||
int footnoteLastListIndex; | |||
int footnoteLastElementIndex; | |||
PageBreakPosition(LayoutManager lm, int iBreakIndex, | |||
int ffli, int ffei, int flli, int flei, | |||
double bpdA, int diff) { | |||
super(lm, iBreakIndex); | |||
bpdAdjust = bpdA; | |||
difference = diff; | |||
footnoteFirstListIndex = ffli; | |||
footnoteFirstElementIndex = ffei; | |||
footnoteLastListIndex = flli; | |||
footnoteLastElementIndex = flei; | |||
} | |||
} | |||
@@ -83,13 +91,13 @@ public abstract class AbstractBreaker { | |||
/** blockListIndex of the current BlockSequence in blockLists */ | |||
private int blockListIndex = 0; | |||
/*LF*/ | |||
/*LF*/ | |||
private List blockLists = null; | |||
private int alignment; | |||
private int alignmentLast; | |||
/*LF*/ | |||
protected MinOptMax footnoteSeparatorLength = new MinOptMax(0); | |||
protected abstract int getCurrentDisplayAlign(); | |||
protected abstract boolean hasMoreContent(); | |||
@@ -107,7 +115,7 @@ public abstract class AbstractBreaker { | |||
//nop | |||
} | |||
protected abstract void finishPart(); | |||
protected abstract void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp); | |||
protected LayoutContext createLayoutContext() { | |||
return new LayoutContext(0); | |||
@@ -157,7 +165,7 @@ public abstract class AbstractBreaker { | |||
log.debug("PLM> start of algorithm (" + this.getClass().getName() | |||
+ "), flow BPD =" + flowBPD); | |||
PageBreakingAlgorithm alg = new PageBreakingAlgorithm(getTopLevelLM(), | |||
alignment, alignmentLast); | |||
alignment, alignmentLast, footnoteSeparatorLength); | |||
int iOptPageNumber; | |||
BlockSequence effectiveList; | |||
@@ -286,7 +294,7 @@ public abstract class AbstractBreaker { | |||
startElementIndex, endElementIndex + 1), childLC); | |||
} | |||
finishPart(); | |||
finishPart(alg, pbp); | |||
startElementIndex = pbp.getLeafPos() + 1; | |||
} |
@@ -521,7 +521,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager { | |||
this.deferredEffectiveList = effectiveList; | |||
} | |||
protected void finishPart() { | |||
protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) { | |||
//nop for bclm | |||
} | |||
@@ -230,8 +230,8 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager | |||
childLC.setRefIPD(ipd); | |||
} else { | |||
// curLM is a ? | |||
childLC.setStackLimit(MinOptMax.subtract(context | |||
.getStackLimit(), stackSize)); | |||
//childLC.setStackLimit(MinOptMax.subtract(context | |||
// .getStackLimit(), stackSize)); | |||
childLC.setRefIPD(referenceIPD); | |||
} | |||
@@ -23,6 +23,9 @@ import org.apache.commons.logging.LogFactory; | |||
import org.apache.fop.traits.MinOptMax; | |||
import java.util.LinkedList; | |||
import java.util.ListIterator; | |||
/** | |||
* The set of nodes is sorted into lines indexed into activeLines. | |||
* The nodes in each line are linked together in a single linked list by the | |||
@@ -43,11 +46,9 @@ public abstract class BreakingAlgorithm { | |||
// penalty value for flagged penalties | |||
private int flaggedPenalty = 50; | |||
// demerit for consecutive lines ending at flagged penalties | |||
private int repeatedFlaggedDemerit = 50; | |||
protected int repeatedFlaggedDemerit = 50; | |||
// demerit for consecutive lines belonging to incompatible fitness classes | |||
private int incompatibleFitnessDemerit = 50; | |||
// suggested modification to the "optimum" number of lines | |||
private int looseness = 0; | |||
protected int incompatibleFitnessDemerit = 50; | |||
/** | |||
* The threshold for considering breaks to be acceptable. | |||
@@ -62,7 +63,7 @@ public abstract class BreakingAlgorithm { | |||
/** | |||
* The width of a line. | |||
*/ | |||
private int lineWidth = 0; | |||
protected int lineWidth = 0; | |||
private boolean force = false; | |||
protected KnuthNode lastDeactivatedNode = null; | |||
@@ -77,7 +78,7 @@ public abstract class BreakingAlgorithm { | |||
/** | |||
* The set of active nodes. | |||
*/ | |||
private KnuthNode[] activeLines; | |||
protected KnuthNode[] activeLines; | |||
/** | |||
* The number of active nodes. | |||
@@ -97,22 +98,22 @@ public abstract class BreakingAlgorithm { | |||
/** | |||
* The total width of all elements handled so far. | |||
*/ | |||
private int totalWidth; | |||
protected int totalWidth; | |||
/** | |||
* The total stretch of all elements handled so far. | |||
*/ | |||
private int totalStretch = 0; | |||
protected int totalStretch = 0; | |||
/** | |||
* The total shrink of all elements handled so far. | |||
*/ | |||
private int totalShrink = 0; | |||
protected int totalShrink = 0; | |||
private BestRecords best; | |||
protected BestRecords best; | |||
private KnuthNode[] positions; | |||
private static final int INFINITE_RATIO = 1000; | |||
protected static final int INFINITE_RATIO = 1000; | |||
protected static Log log = LogFactory.getLog(KnuthParagraph.class); | |||
@@ -126,7 +127,7 @@ public abstract class BreakingAlgorithm { | |||
// this class represent a feasible breaking point | |||
public class KnuthNode { | |||
protected class KnuthNode { | |||
// index of the breakpoint represented by this node | |||
public int position; | |||
@@ -198,7 +199,7 @@ public abstract class BreakingAlgorithm { | |||
// this class stores information about how the nodes | |||
// which could start a line | |||
// ending at the current element | |||
private class BestRecords { | |||
protected class BestRecords { | |||
private static final double INFINITE_DEMERITS = Double.POSITIVE_INFINITY; | |||
//private static final double INFINITE_DEMERITS = 1E11; | |||
@@ -215,7 +216,8 @@ public abstract class BreakingAlgorithm { | |||
} | |||
public void addRecord(double demerits, KnuthNode node, double adjust, | |||
int availableShrink, int availableStretch, int difference, int fitness) { | |||
int availableShrink, int availableStretch, | |||
int difference, int fitness) { | |||
if (demerits > bestDemerits[fitness]) { | |||
log.error("New demerits value greter than the old one"); | |||
} | |||
@@ -274,11 +276,7 @@ public abstract class BreakingAlgorithm { | |||
public void reset() { | |||
for (int i = 0; i < 4; i ++) { | |||
bestDemerits[i] = INFINITE_DEMERITS; | |||
bestNode[i] = null; | |||
bestAdjust[i] = 0.0; | |||
bestDifference[i] = 0; | |||
bestAvailableShrink[i] = 0; | |||
bestAvailableStretch[i] = 0; | |||
// there is no need to reset the other arrays | |||
} | |||
bestIndex = -1; | |||
} | |||
@@ -299,9 +297,7 @@ public abstract class BreakingAlgorithm { | |||
this.threshold = threshold; | |||
this.force = force; | |||
this.lineWidth = lineWidth; | |||
this.totalWidth = 0; | |||
this.totalStretch = 0; | |||
this.totalShrink = 0; | |||
initialize(); | |||
activeLines = new KnuthNode[20]; | |||
@@ -324,7 +320,7 @@ public abstract class BreakingAlgorithm { | |||
// create an active node representing the starting point | |||
activeLines = new KnuthNode[20]; | |||
addNode(0, new KnuthNode(firstBoxIndex, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, null)); | |||
addNode(0, createNode(firstBoxIndex, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, null)); | |||
if (log.isTraceEnabled()) { | |||
log.trace("Looping over " + par.size() + " box objects"); | |||
@@ -339,6 +335,7 @@ public abstract class BreakingAlgorithm { | |||
// a KnuthBox object is not a legal line break | |||
totalWidth += thisElement.getW(); | |||
previousIsBox = true; | |||
handleBox((KnuthBox) thisElement); | |||
} else if (thisElement.isGlue()) { | |||
// a KnuthGlue object is a legal line break | |||
// only if the previous object is a KnuthBox | |||
@@ -372,15 +369,8 @@ public abstract class BreakingAlgorithm { | |||
} | |||
log.debug("Restarting at node " + lastForced); | |||
lastForced.totalDemerits = 0; | |||
addNode(lastForced.line, lastForced); | |||
restartFrom(lastForced, i); | |||
i = lastForced.position; | |||
startLine = lastForced.line; | |||
endLine = startLine + 1; | |||
totalWidth = lastForced.totalWidth; | |||
totalStretch = lastForced.totalStretch; | |||
totalShrink = lastForced.totalShrink; | |||
lastTooShort = lastTooLong = null; | |||
} | |||
} | |||
if (log.isTraceEnabled()) { | |||
@@ -404,6 +394,44 @@ public abstract class BreakingAlgorithm { | |||
return line; | |||
} | |||
protected void initialize() { | |||
this.totalWidth = 0; | |||
this.totalStretch = 0; | |||
this.totalShrink = 0; | |||
} | |||
protected KnuthNode createNode(int position, int line, int fitness, | |||
int totalWidth, int totalStretch, int totalShrink, | |||
double adjustRatio, int availableShrink, int availableStretch, int difference, | |||
double totalDemerits, KnuthNode previous) { | |||
return new KnuthNode(position, line, fitness, | |||
totalWidth, totalStretch, totalShrink, | |||
adjustRatio, availableShrink, availableStretch, | |||
difference, totalDemerits, previous); | |||
} | |||
protected KnuthNode createNode(int position, int line, int fitness, | |||
int totalWidth, int totalStretch, int totalShrink) { | |||
return new KnuthNode(position, line, fitness, | |||
totalWidth, totalStretch, totalShrink, | |||
best.getAdjust(fitness), best.getAvailableShrink(fitness), best.getAvailableStretch(fitness), | |||
best.getDifference(fitness), best.getDemerits(fitness), best.getNode(fitness)); | |||
} | |||
protected void handleBox(KnuthBox box) { | |||
} | |||
protected void restartFrom(KnuthNode restartingNode, int currentIndex) { | |||
restartingNode.totalDemerits = 0; | |||
addNode(restartingNode.line, restartingNode); | |||
startLine = restartingNode.line; | |||
endLine = startLine + 1; | |||
totalWidth = restartingNode.totalWidth; | |||
totalStretch = restartingNode.totalStretch; | |||
totalShrink = restartingNode.totalShrink; | |||
lastTooShort = lastTooLong = null; | |||
} | |||
private void considerLegalBreak(KnuthElement element, int elementIdx) { | |||
if (log.isTraceEnabled()) { | |||
@@ -462,7 +490,7 @@ public abstract class BreakingAlgorithm { | |||
double demerits = computeDemerits(node, element, fitnessClass, r); | |||
if (r <= -1) { | |||
if (lastTooLong == null || demerits < lastTooLong.totalDemerits) { | |||
lastTooLong = new KnuthNode(elementIdx, line + 1, fitnessClass, | |||
lastTooLong = createNode(elementIdx, line + 1, fitnessClass, | |||
totalWidth, totalStretch, totalShrink, | |||
r, availableShrink, availableStretch, | |||
difference, demerits, node); | |||
@@ -472,7 +500,7 @@ public abstract class BreakingAlgorithm { | |||
} | |||
} else { | |||
if (lastTooShort == null || demerits <= lastTooShort.totalDemerits) { | |||
lastTooShort = new KnuthNode(elementIdx, line + 1, fitnessClass, | |||
lastTooShort = createNode(elementIdx, line + 1, fitnessClass, | |||
totalWidth, totalStretch, totalShrink, | |||
r, availableShrink, availableStretch, | |||
difference, demerits, node); | |||
@@ -518,14 +546,8 @@ public abstract class BreakingAlgorithm { | |||
if (log.isTraceEnabled()) { | |||
log.trace("\tInsert new break in list of " + activeNodeCount); | |||
} | |||
KnuthNode newNode = new KnuthNode(elementIdx, line + 1, i, | |||
newWidth, newStretch, newShrink, | |||
best.getAdjust(i), | |||
best.getAvailableShrink(i), | |||
best.getAvailableStretch(i), | |||
best.getDifference(i), | |||
best.getDemerits(i), | |||
best.getNode(i)); | |||
KnuthNode newNode = createNode(elementIdx, line + 1, i, | |||
newWidth, newStretch, newShrink); | |||
addNode(line + 1, newNode); | |||
} | |||
} | |||
@@ -540,7 +562,7 @@ public abstract class BreakingAlgorithm { | |||
* @return The difference in width. Positive numbers mean extra space in the line, | |||
* negative number that the line overflows. | |||
*/ | |||
private int computeDifference(KnuthNode activeNode, KnuthElement element) { | |||
protected int computeDifference(KnuthNode activeNode, KnuthElement element) { | |||
// compute the adjustment ratio | |||
int actualWidth = totalWidth - activeNode.totalWidth; | |||
if (element.isPenalty()) { | |||
@@ -563,7 +585,7 @@ public abstract class BreakingAlgorithm { | |||
* @param difference | |||
* @return The ration. | |||
*/ | |||
private double computeAdjustmentRatio(KnuthNode activeNode, int difference) { | |||
protected double computeAdjustmentRatio(KnuthNode activeNode, int difference) { | |||
// compute the adjustment ratio | |||
if (difference > 0) { | |||
int maxAdjustment = totalStretch - activeNode.totalStretch; | |||
@@ -603,7 +625,7 @@ public abstract class BreakingAlgorithm { | |||
} | |||
} | |||
private double computeDemerits(KnuthNode activeNode, KnuthElement element, | |||
protected double computeDemerits(KnuthNode activeNode, KnuthElement element, | |||
int fitnessClass, double r) { | |||
double demerits = 0; | |||
// compute demerits | |||
@@ -639,7 +661,7 @@ public abstract class BreakingAlgorithm { | |||
* @param idx index of the element. | |||
* @return | |||
*/ | |||
private KnuthElement getElement(int idx) { | |||
protected KnuthElement getElement(int idx) { | |||
return (KnuthElement) par.get(idx); | |||
} | |||
@@ -52,7 +52,6 @@ public class FlowLayoutManager extends BlockStackingLayoutManager | |||
*/ | |||
private int numSubsequentOverflows = 0; | |||
/*LF*/ | |||
private static class StackingIter extends PositionIterator { | |||
StackingIter(Iterator parentIter) { | |||
super(parentIter); | |||
@@ -66,7 +65,6 @@ public class FlowLayoutManager extends BlockStackingLayoutManager | |||
return ((Position) nextObj); | |||
} | |||
} | |||
/*LF*/ | |||
/** | |||
* This is the top level layout manager. | |||
@@ -223,15 +221,14 @@ public class FlowLayoutManager extends BlockStackingLayoutManager | |||
returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false)); | |||
} | |||
} | |||
/*LF*/ if (returnedList.size() > 0) { // controllare! | |||
if (returnedList.size() > 0) { // controllare! | |||
returnList.addAll(returnedList); | |||
if (((KnuthElement)returnedList.getLast()).isPenalty() | |||
&& ((KnuthPenalty)returnedList.getLast()).getP() == -KnuthElement.INFINITE) { | |||
// a descendant of this flow has break-after | |||
/*LF*/ //System.out.println("FLM - break after!!"); | |||
return returnList; | |||
} | |||
/*LF*/ } | |||
} | |||
} | |||
prevLM = curLM; | |||
} | |||
@@ -294,8 +291,6 @@ public class FlowLayoutManager extends BlockStackingLayoutManager | |||
KnuthElement currElement = null; | |||
int fromIndex = 0; | |||
/*LF*/ //System.out.println(""); | |||
/*LF*/ //System.out.println("FLM.getChangedKnuthElements> prima dell'unwrap, oldList.size() = " + oldList.size() + " da 0 a " + (oldList.size() - 1)); | |||
// "unwrap" the Positions stored in the elements | |||
KnuthElement oldElement; | |||
while (oldListIterator.hasNext()) { | |||
@@ -311,11 +306,9 @@ public class FlowLayoutManager extends BlockStackingLayoutManager | |||
// reset the iterator | |||
oldListIterator = oldList.listIterator(); | |||
/*LF*/ //System.out.println("FLM.getChangedKnuthElements> dopo l'unwrap, oldList.size() = " + oldList.size() + " da 0 a " + (oldList.size() - 1)); | |||
while (oldListIterator.hasNext()) { | |||
currElement = (KnuthElement) oldListIterator.next(); | |||
/*LF*/ //System.out.println("elemento n. " + oldListIterator.previousIndex() + " nella oldList"); | |||
if (prevElement != null | |||
&& prevElement.getLayoutManager() != currElement.getLayoutManager()) { | |||
// prevElement is the last element generated by the same LM | |||
@@ -323,7 +316,6 @@ public class FlowLayoutManager extends BlockStackingLayoutManager | |||
prevElement.getLayoutManager(); | |||
BlockLevelLayoutManager currLM = (BlockLevelLayoutManager) | |||
currElement.getLayoutManager(); | |||
/*LF*/ //System.out.println("FLM.getChangedKnuthElements> chiamata da " + fromIndex + " a " + oldListIterator.previousIndex()); | |||
returnedList.addAll(prevLM.getChangedKnuthElements(oldList.subList(fromIndex, oldListIterator.previousIndex()), | |||
/*flaggedPenalty,*/ alignment)); | |||
fromIndex = oldListIterator.previousIndex(); | |||
@@ -343,7 +335,6 @@ public class FlowLayoutManager extends BlockStackingLayoutManager | |||
if (currElement != null) { | |||
BlockLevelLayoutManager currLM = (BlockLevelLayoutManager) | |||
currElement.getLayoutManager(); | |||
/*LF*/ //System.out.println("FLM.getChangedKnuthElements> chiamata da " + fromIndex + " a " + oldList.size()); | |||
returnedList.addAll(currLM.getChangedKnuthElements(oldList.subList(fromIndex, oldList.size()), | |||
/*flaggedPenalty,*/ alignment)); | |||
} | |||
@@ -416,6 +407,7 @@ public class FlowLayoutManager extends BlockStackingLayoutManager | |||
* @see org.apache.fop.layoutmgr.LayoutManager#addChildArea(Area) | |||
*/ | |||
public void addChildArea(Area childArea) { | |||
getParentArea(childArea); | |||
addChildToArea(childArea, | |||
this.currentAreas[childArea.getAreaClass()]); | |||
} | |||
@@ -438,7 +430,7 @@ public class FlowLayoutManager extends BlockStackingLayoutManager | |||
"area class (" + aclass + ") requested."); | |||
} | |||
this.currentAreas[parentArea.getAreaClass()] = parentArea; | |||
this.currentAreas[aclass] = parentArea; | |||
setCurrentArea(parentArea); | |||
return parentArea; | |||
} |
@@ -603,6 +603,7 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager | |||
returnedElement.getPosition())); | |||
returnList.add(returnedElement); | |||
} | |||
setFinished(curLM.isFinished() && (getChildLM() == null)); | |||
return returnList; | |||
} else { | |||
// curLM returned null because it finished; |
@@ -20,15 +20,46 @@ package org.apache.fop.layoutmgr; | |||
import org.apache.fop.traits.MinOptMax; | |||
import java.util.LinkedList; | |||
public class KnuthBlockBox extends KnuthBox { | |||
private MinOptMax ipdRange; | |||
private int bpd; | |||
private LinkedList footnoteList; | |||
private LinkedList elementLists = null; | |||
public KnuthBlockBox(int w, MinOptMax range, int bpdim, Position pos, boolean bAux) { | |||
super(w, pos, bAux); | |||
ipdRange = (MinOptMax) range.clone(); | |||
bpd = bpdim; | |||
footnoteList = new LinkedList(); | |||
} | |||
public KnuthBlockBox(int w, LinkedList list, Position pos, boolean bAux) { | |||
super(w, pos, bAux); | |||
ipdRange = new MinOptMax(0); | |||
bpd = 0; | |||
footnoteList = new LinkedList(list); | |||
} | |||
public LinkedList getFootnoteBodyLMs() { | |||
return footnoteList; | |||
} | |||
public boolean hasAnchors() { | |||
return (footnoteList.size() > 0); | |||
} | |||
public void addElementList(LinkedList list) { | |||
if (elementLists == null) { | |||
elementLists = new LinkedList(); | |||
} | |||
elementLists.add(list); | |||
} | |||
public LinkedList getElementLists() { | |||
return elementLists; | |||
} | |||
public MinOptMax getIPDRange() { |
@@ -23,6 +23,7 @@ public class KnuthInlineBox extends KnuthBox { | |||
private int lead; | |||
private int total; | |||
private int middle; | |||
private FootnoteBodyLayoutManager footnoteBodyLM = null; | |||
/** | |||
* Create a new KnuthBox. | |||
@@ -61,4 +62,25 @@ public class KnuthInlineBox extends KnuthBox { | |||
public int getMiddle() { | |||
return middle; | |||
} | |||
/** | |||
* @param fblm the FootnoteBodyLM this box must hold a reference to | |||
*/ | |||
public void setFootnoteBodyLM(FootnoteBodyLayoutManager fblm) { | |||
footnoteBodyLM = fblm; | |||
} | |||
/** | |||
* @return the FootnoteBodyLM this box holds a reference to | |||
*/ | |||
public FootnoteBodyLayoutManager getFootnoteBodyLM() { | |||
return footnoteBodyLM; | |||
} | |||
/** | |||
* @return true if this box holds a reference to a FootnoteBodyLM | |||
*/ | |||
public boolean isAnchor() { | |||
return (footnoteBodyLM != null); | |||
} | |||
} |
@@ -23,7 +23,7 @@ import org.apache.fop.fo.pagination.PageSequence; | |||
import org.apache.fop.fo.pagination.SideRegion; | |||
import org.apache.fop.fo.pagination.StaticContent; | |||
import org.apache.fop.area.AreaTreeHandler; | |||
import org.apache.fop.area.Block; | |||
/** | |||
* The interface for all LayoutManager makers | |||
@@ -68,5 +68,15 @@ public interface LayoutManagerMaker { | |||
public StaticContentLayoutManager makeStaticContentLayoutManager( | |||
PageSequenceLayoutManager pslm, StaticContent sc, SideRegion reg); | |||
/** | |||
* Make a StaticContentLayoutManager object for a footnote-separator. | |||
* @param pslm the parent PageSequenceLayoutManager object | |||
* @param sc the fo:static-content object this SCLM will process | |||
* @param block the Block area this SCLM must add its areas to | |||
* @return The created StaticContentLayoutManager object | |||
*/ | |||
public StaticContentLayoutManager makeStaticContentLayoutManager( | |||
PageSequenceLayoutManager pslm, StaticContent sc, Block block); | |||
} | |||
@@ -166,6 +166,14 @@ public class LayoutManagerMapping implements LayoutManagerMaker { | |||
return new StaticContentLayoutManager(pslm, sc, reg); | |||
} | |||
/* | |||
* @see org.apache.fop.layoutmgr.LayoutManagerMaker#makeStaticContentLayoutManager(org.apache.fop.layoutmgr.PageSequenceLayoutManager, org.apache.fop.fo.pagination.StaticContent, org.apache.fop.area.Block) | |||
*/ | |||
public StaticContentLayoutManager makeStaticContentLayoutManager( | |||
PageSequenceLayoutManager pslm, StaticContent sc, org.apache.fop.area.Block block) { | |||
return new StaticContentLayoutManager(pslm, sc, block); | |||
} | |||
public static class Maker { | |||
public void make(FONode node, List lms) { | |||
// no layout manager | |||
@@ -230,10 +238,7 @@ public class LayoutManagerMapping implements LayoutManagerMaker { | |||
public static class FootnodeLayoutManagerMaker extends Maker { | |||
public void make(FONode node, List lms) { | |||
Inline citation = ((Footnote) node).getInlineFO(); | |||
if (citation != null) { | |||
lms.add(new InlineLayoutManager(citation)); | |||
} | |||
lms.add(new FootnoteLayoutManager((Footnote) node)); | |||
} | |||
} | |||
@@ -620,6 +620,12 @@ public class LineLayoutManager extends InlineStackingLayoutManager | |||
knuthPar.addAll(((InlineLevelLayoutManager) | |||
prevBox.getLayoutManager()) | |||
.addALetterSpaceTo(oldList)); | |||
if (((KnuthInlineBox) prevBox).isAnchor()) { | |||
// prevBox represents a footnote citation: copy footnote info | |||
// from prevBox to the new box | |||
KnuthInlineBox newBox = (KnuthInlineBox) knuthPar.getLast(); | |||
newBox.setFootnoteBodyLM(((KnuthInlineBox) prevBox).getFootnoteBodyLM()); | |||
} | |||
} | |||
// look at the last element | |||
@@ -941,6 +947,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager | |||
/* "normal" vertical alignment: create a sequence whose boxes | |||
represent effective lines, and contain LineBreakPositions */ | |||
Position returnPosition = new LeafPosition(this, p); | |||
int startIndex = 0; | |||
for (int i = 0; | |||
i < lineLayouts.getChosenLineCount(); | |||
i++) { | |||
@@ -951,8 +958,21 @@ public class LineLayoutManager extends InlineStackingLayoutManager | |||
// null penalty allowing a page break between lines | |||
returnList.add(new KnuthPenalty(0, 0, false, returnPosition, false)); | |||
} | |||
returnList.add(new KnuthBox(((LineBreakPosition) lineLayouts.getChosenPosition(i)).lineHeight, | |||
lineLayouts.getChosenPosition(i), false)); | |||
int endIndex = ((LineBreakPosition) lineLayouts.getChosenPosition(i)).getLeafPos(); | |||
// create a list of the FootnoteBodyLM handling footnotes | |||
// whose citations are in this line | |||
LinkedList footnoteList = new LinkedList(); | |||
ListIterator elementIterator = ((Paragraph) knuthParagraphs.get(p)).listIterator(startIndex); | |||
while (elementIterator.nextIndex() <= endIndex) { | |||
KnuthElement element = (KnuthElement) elementIterator.next(); | |||
if (element instanceof KnuthInlineBox | |||
&& ((KnuthInlineBox) element).isAnchor()) { | |||
footnoteList.add(((KnuthInlineBox) element).getFootnoteBodyLM()); | |||
} | |||
} | |||
startIndex = endIndex + 1; | |||
returnList.add(new KnuthBlockBox(((LineBreakPosition) lineLayouts.getChosenPosition(i)).lineHeight, | |||
footnoteList, lineLayouts.getChosenPosition(i), false)); | |||
} | |||
} | |||
} |
@@ -18,18 +18,458 @@ | |||
package org.apache.fop.layoutmgr; | |||
import java.util.ArrayList; | |||
import java.util.LinkedList; | |||
import java.util.ListIterator; | |||
import org.apache.fop.layoutmgr.AbstractBreaker.PageBreakPosition; | |||
import org.apache.fop.traits.MinOptMax; | |||
class PageBreakingAlgorithm extends BreakingAlgorithm { | |||
private LayoutManager topLevelLM; | |||
private LinkedList pageBreaks = null; | |||
private ArrayList footnotesList = null; | |||
private ArrayList lengthList = null; | |||
private int totalFootnotesLength = 0; | |||
private int insertedFootnotesLength = 0; | |||
private boolean bPendingFootnotes = false; | |||
private int footnoteListIndex = 0; | |||
private int footnoteElementIndex = -1; | |||
// demerits for a page break that splits a footnote | |||
private int splitFootnoteDemerits = 5000; | |||
// demerits for a page break that defers a whole footnote to the following page | |||
private int deferredFootnoteDemerits = 10000; | |||
private MinOptMax footnoteSeparatorLength = new MinOptMax(0); | |||
public PageBreakingAlgorithm(LayoutManager topLevelLM, | |||
int alignment, int alignmentLast) { | |||
int alignment, int alignmentLast, | |||
MinOptMax fnSeparatorLength) { | |||
super(alignment, alignmentLast, true); | |||
this.topLevelLM = topLevelLM; | |||
best = new BestPageRecords(); | |||
footnoteSeparatorLength = (MinOptMax) fnSeparatorLength.clone(); | |||
// add some stretch, to avoid a restart for every page containing footnotes | |||
if (footnoteSeparatorLength.min == footnoteSeparatorLength.max) { | |||
footnoteSeparatorLength.max += 10000; | |||
} | |||
} | |||
/** | |||
* this class represent a feasible breaking point | |||
* with extra information about footnotes | |||
*/ | |||
protected class KnuthPageNode extends KnuthNode { | |||
// additional length due to footnotes | |||
public int totalFootnotes; | |||
// index of the last inserted footnote | |||
public int footnoteListIndex; | |||
// index of the last inserted element of the last inserted footnote | |||
public int footnoteElementIndex; | |||
public KnuthPageNode(int position, int line, int fitness, | |||
int totalWidth, int totalStretch, int totalShrink, | |||
int totalFootnotes, int footnoteListIndex, int footnoteElementIndex, | |||
double adjustRatio, int availableShrink, int availableStretch, | |||
int difference, double totalDemerits, KnuthNode previous) { | |||
super(position, line, fitness, | |||
totalWidth, totalStretch, totalShrink, | |||
adjustRatio, availableShrink, availableStretch, | |||
difference, totalDemerits, previous); | |||
this.totalFootnotes = totalFootnotes; | |||
this.footnoteListIndex = footnoteListIndex; | |||
this.footnoteElementIndex = footnoteElementIndex; | |||
} | |||
} | |||
/** | |||
* this class stores information about how the nodes | |||
* which could start a line ending at the current element | |||
*/ | |||
protected class BestPageRecords extends BestRecords { | |||
private int bestFootnotesLength[] = new int[4]; | |||
private int bestFootnoteListIndex[] = new int[4]; | |||
private int bestFootnoteElementIndex[] = new int[4]; | |||
public void addRecord(double demerits, KnuthNode node, double adjust, | |||
int availableShrink, int availableStretch, | |||
int difference, int fitness) { | |||
super.addRecord(demerits, node, adjust, | |||
availableShrink, availableStretch, | |||
difference, fitness); | |||
bestFootnotesLength[fitness] = insertedFootnotesLength; | |||
bestFootnoteListIndex[fitness] = footnoteListIndex; | |||
bestFootnoteElementIndex[fitness] = footnoteElementIndex; | |||
} | |||
public int getFootnotesLength(int fitness) { | |||
return bestFootnotesLength[fitness]; | |||
} | |||
public int getFootnoteListIndex(int fitness) { | |||
return bestFootnoteListIndex[fitness]; | |||
} | |||
public int getFootnoteElementIndex(int fitness) { | |||
return bestFootnoteElementIndex[fitness]; | |||
} | |||
} | |||
protected void initialize() { | |||
super.initialize(); | |||
insertedFootnotesLength = 0; | |||
footnoteListIndex = 0; | |||
footnoteElementIndex = -1; | |||
} | |||
protected KnuthNode createNode(int position, int line, int fitness, | |||
int totalWidth, int totalStretch, int totalShrink, | |||
double adjustRatio, int availableShrink, int availableStretch, | |||
int difference, double totalDemerits, KnuthNode previous) { | |||
return new KnuthPageNode(position, line, fitness, | |||
totalWidth, totalStretch, totalShrink, | |||
insertedFootnotesLength, footnoteListIndex, footnoteElementIndex, | |||
adjustRatio, availableShrink, availableStretch, | |||
difference, totalDemerits, previous); | |||
} | |||
protected KnuthNode createNode(int position, int line, int fitness, | |||
int totalWidth, int totalStretch, int totalShrink) { | |||
return new KnuthPageNode(position, line, fitness, | |||
totalWidth, totalStretch, totalShrink, | |||
((BestPageRecords) best).getFootnotesLength(fitness), | |||
((BestPageRecords) best).getFootnoteListIndex(fitness), | |||
((BestPageRecords) best).getFootnoteElementIndex(fitness), | |||
best.getAdjust(fitness), best.getAvailableShrink(fitness), best.getAvailableStretch(fitness), | |||
best.getDifference(fitness), best.getDemerits(fitness), best.getNode(fitness)); | |||
} | |||
protected void handleBox(KnuthBox box) { | |||
if (box instanceof KnuthBlockBox | |||
&& ((KnuthBlockBox) box).hasAnchors()) { | |||
handleFootnotes(((KnuthBlockBox) box).getElementLists()); | |||
} | |||
} | |||
private void handleFootnotes(LinkedList elementLists) { | |||
// initialization | |||
if (!bPendingFootnotes) { | |||
bPendingFootnotes = true; | |||
footnotesList = new ArrayList(); | |||
lengthList = new ArrayList(); | |||
totalFootnotesLength = 0; | |||
} | |||
// compute the total length of the footnotes | |||
ListIterator elementListsIterator = elementLists.listIterator(); | |||
while (elementListsIterator.hasNext()) { | |||
LinkedList noteList = (LinkedList) elementListsIterator.next(); | |||
int noteLength = 0; | |||
footnotesList.add(noteList); | |||
ListIterator noteListIterator = noteList.listIterator(); | |||
while (noteListIterator.hasNext()) { | |||
KnuthElement element = (KnuthElement) noteListIterator.next(); | |||
if (element.isBox() || element.isGlue()) { | |||
noteLength += element.getW(); | |||
} | |||
} | |||
int prevLength = (lengthList.size() == 0 ? 0 : ((Integer) lengthList.get(lengthList.size() - 1)).intValue()); | |||
lengthList.add(new Integer(prevLength + noteLength)); | |||
totalFootnotesLength += noteLength; | |||
} | |||
} | |||
protected void restartFrom(KnuthNode restartingNode, int currentIndex) { | |||
super.restartFrom(restartingNode, currentIndex); | |||
if (bPendingFootnotes) { | |||
// remove from footnotesList the note lists that will be met | |||
// after the restarting point | |||
for (int j = currentIndex; j >= restartingNode.position; j--) { | |||
KnuthElement resettedElement = getElement(j); | |||
if (resettedElement instanceof KnuthBlockBox | |||
&& ((KnuthBlockBox) resettedElement).hasAnchors()) { | |||
resetFootnotes(((KnuthBlockBox) resettedElement).getElementLists()); | |||
} | |||
} | |||
} | |||
} | |||
private void resetFootnotes(LinkedList elementLists) { | |||
for (int i = 0; i < elementLists.size(); i++) { | |||
LinkedList removedList = (LinkedList) footnotesList.remove(footnotesList.size() - 1); | |||
lengthList.remove(lengthList.size() - 1); | |||
// update totalFootnotesLength | |||
if (lengthList.size() > 0) { | |||
totalFootnotesLength = ((Integer) lengthList.get(lengthList.size() - 1)).intValue(); | |||
} else { | |||
totalFootnotesLength = 0; | |||
} | |||
} | |||
// update bPendingFootnotes; | |||
if (footnotesList.size() == 0) { | |||
bPendingFootnotes = false; | |||
} | |||
} | |||
/** | |||
* Return the difference between the line width and the width of the break that | |||
* ends in 'element'. | |||
* @param activeNode | |||
* @param element | |||
* @return The difference in width. Positive numbers mean extra space in the line, | |||
* negative number that the line overflows. | |||
*/ | |||
protected int computeDifference(KnuthNode activeNode, KnuthElement element) { | |||
int actualWidth = totalWidth - activeNode.totalWidth; | |||
if (element.isPenalty()) { | |||
actualWidth += element.getW(); | |||
} | |||
if (bPendingFootnotes) { | |||
int newFootnotes = totalFootnotesLength - ((KnuthPageNode) activeNode).totalFootnotes; | |||
// add the footnote separator width if some footnote content will be added | |||
if (((KnuthPageNode) activeNode).totalFootnotes < totalFootnotesLength) { | |||
actualWidth += footnoteSeparatorLength.opt; | |||
} | |||
if (actualWidth + newFootnotes <= lineWidth) { | |||
// there is enough space to insert all footnotes: | |||
// add the whole newFootnotes length | |||
actualWidth += newFootnotes; | |||
insertedFootnotesLength = ((KnuthPageNode) activeNode).totalFootnotes + newFootnotes; | |||
footnoteListIndex = footnotesList.size() - 1; | |||
footnoteElementIndex = ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1; | |||
} else { | |||
// check if there is enough space to insert at least a piece | |||
// of the last footnote (and all the previous ones) | |||
int footnoteSplit = getFootnoteSplit((KnuthPageNode) activeNode, lineWidth - actualWidth); | |||
if (actualWidth + footnoteSplit <= lineWidth) { | |||
// the last footnote will be split | |||
// add more footnote content as possible | |||
actualWidth += footnoteSplit; | |||
insertedFootnotesLength = ((KnuthPageNode) activeNode).totalFootnotes + footnoteSplit; | |||
footnoteListIndex = footnotesList.size() - 1; | |||
// footnoteElementIndex has been set in getFootnoteSplit() | |||
} else { | |||
// there is no space to add the smallest piece of the last footnote: | |||
// add the whole newFootnotes length, so this breakpoint will be discarded | |||
actualWidth += newFootnotes; | |||
insertedFootnotesLength = ((KnuthPageNode) activeNode).totalFootnotes + newFootnotes; | |||
footnoteListIndex = footnotesList.size() - 1; | |||
footnoteElementIndex = ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1; | |||
} | |||
} | |||
} | |||
return lineWidth - actualWidth; | |||
} | |||
private int getFootnoteSplit(KnuthPageNode activeNode, int availableLength) { | |||
// length of all the not-yet-inserted footnotes | |||
int newFootnotes = totalFootnotesLength - activeNode.totalFootnotes; | |||
if (availableLength <= 0) { | |||
return newFootnotes; | |||
} else { | |||
// the split must contain a piece of the last footnote | |||
// together with all previous, not yet inserted footnotes | |||
int firstFootnoteIndex = activeNode.footnoteListIndex; | |||
int firstElementIndex = activeNode.footnoteElementIndex; | |||
if (firstElementIndex == ((LinkedList) footnotesList.get(firstFootnoteIndex)).size() - 1) { | |||
// advance to the next list | |||
firstFootnoteIndex ++; | |||
firstElementIndex = 0; | |||
} else { | |||
firstElementIndex ++; | |||
} | |||
int splitLength = 0; | |||
ListIterator noteListIterator = null; | |||
KnuthElement element = null; | |||
// add previous notes | |||
if (footnotesList.size() > 1) { | |||
splitLength = ((Integer) lengthList.get(footnotesList.size() - 2)).intValue() | |||
- activeNode.totalFootnotes; | |||
} | |||
// add a split of the last note | |||
noteListIterator = ((LinkedList) footnotesList.get(footnotesList.size() - 1)).listIterator(); | |||
boolean bSomethingAdded = false; | |||
int prevSplitLength = 0; | |||
int prevIndex = 0; | |||
int index = 0; | |||
while (!(bSomethingAdded && splitLength > availableLength)) { | |||
if (!bSomethingAdded) { | |||
bSomethingAdded = true; | |||
} else { | |||
prevSplitLength = splitLength; | |||
prevIndex = index; | |||
} | |||
// get a sub-sequence from the note element list | |||
boolean bPrevIsBox = false; | |||
while (noteListIterator.hasNext()) { | |||
element = (KnuthElement) noteListIterator.next(); | |||
if (element.isBox()) { | |||
// element is a box | |||
splitLength += element.getW(); | |||
bPrevIsBox = true; | |||
} else if (element.isGlue()) { | |||
// element is a glue | |||
if (bPrevIsBox) { | |||
// end of the sub-sequence | |||
index = noteListIterator.previousIndex(); | |||
break; | |||
} | |||
bPrevIsBox = false; | |||
splitLength += element.getW(); | |||
} else { | |||
// element is a penalty | |||
if (element.getP() < KnuthElement.INFINITE) { | |||
index = noteListIterator.previousIndex(); | |||
break; | |||
} | |||
} | |||
} | |||
} | |||
// if prevSplitLength is 0, this means that the available length isn't enough | |||
// to insert even the smallest split of the last footnote, so we cannot end a | |||
// page here | |||
// if prevSplitLength is > 0 we can insert some footnote content in this page | |||
// and insert the remaining in the following one | |||
if (prevSplitLength > 0 ) { | |||
footnoteElementIndex = prevIndex; | |||
return prevSplitLength; | |||
} else { | |||
return newFootnotes; | |||
} | |||
} | |||
} | |||
/** | |||
* Return the adjust ration needed to make up for the difference. A ration of | |||
* <ul> | |||
* <li>0 means that the break has the exact right width</li> | |||
* <li>>= -1 && < 0 means that the break is to wider than the line, | |||
* but within the minimim values of the glues.</li> | |||
* <li>>0 && < 1 means that the break is smaller than the line width, | |||
* but within the maximum values of the glues.</li> | |||
* <li>> 1 means that the break is too small to make up for the glues.</li> | |||
* </ul> | |||
* @param activeNode | |||
* @param difference | |||
* @return The ration. | |||
*/ | |||
protected double computeAdjustmentRatio(KnuthNode activeNode, int difference) { | |||
// compute the adjustment ratio | |||
if (difference > 0) { | |||
int maxAdjustment = totalStretch - activeNode.totalStretch; | |||
// add the footnote separator stretch if some footnote content will be added | |||
if (((KnuthPageNode) activeNode).totalFootnotes < totalFootnotesLength) { | |||
maxAdjustment += footnoteSeparatorLength.max - footnoteSeparatorLength.opt; | |||
} | |||
if (maxAdjustment > 0) { | |||
return (double) difference / maxAdjustment; | |||
} else { | |||
return INFINITE_RATIO; | |||
} | |||
} else if (difference < 0) { | |||
int maxAdjustment = totalShrink - activeNode.totalShrink; | |||
// add the footnote separator shrink if some footnote content will be added | |||
if (((KnuthPageNode) activeNode).totalFootnotes < totalFootnotesLength) { | |||
maxAdjustment += footnoteSeparatorLength.opt - footnoteSeparatorLength.min; | |||
} | |||
if (maxAdjustment > 0) { | |||
return (double) difference / maxAdjustment; | |||
} else { | |||
return -INFINITE_RATIO; | |||
} | |||
} else { | |||
return 0; | |||
} | |||
} | |||
protected double computeDemerits(KnuthNode activeNode, KnuthElement element, | |||
int fitnessClass, double r) { | |||
double demerits = 0; | |||
// compute demerits | |||
double f = Math.abs(r); | |||
f = 1 + 100 * f * f * f; | |||
if (element.isPenalty() && element.getP() >= 0) { | |||
f += element.getP(); | |||
demerits = f * f; | |||
} else if (element.isPenalty() && !element.isForcedBreak()) { | |||
double penalty = element.getP(); | |||
demerits = f * f - penalty * penalty; | |||
} else { | |||
demerits = f * f; | |||
} | |||
if (element.isPenalty() && ((KnuthPenalty) element).isFlagged() | |||
&& getElement(activeNode.position).isPenalty() | |||
&& ((KnuthPenalty) getElement(activeNode.position)).isFlagged()) { | |||
// add demerit for consecutive breaks at flagged penalties | |||
demerits += repeatedFlaggedDemerit; | |||
} | |||
if (Math.abs(fitnessClass - activeNode.fitness) > 1) { | |||
// add demerit for consecutive breaks | |||
// with very different fitness classes | |||
demerits += incompatibleFitnessDemerit; | |||
} | |||
if (bPendingFootnotes) { | |||
if (footnoteListIndex < footnotesList.size() - 1) { | |||
// add demerits for the deferred footnotes | |||
demerits += deferredFootnoteDemerits; | |||
} else if (footnoteElementIndex < ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1) { | |||
// add demerits for the footnote split between pages | |||
demerits += splitFootnoteDemerits; | |||
} else { | |||
// reduce demerits when all footnotes are inserted in the page where their citations are | |||
demerits /= 2; | |||
} | |||
} | |||
demerits += activeNode.totalDemerits; | |||
return demerits; | |||
} | |||
/** | |||
* Remove the first node in line 'line'. If the line then becomes empty, adjust the | |||
* startLine accordingly. | |||
* @param line | |||
* @param node | |||
*/ | |||
protected void removeNode(int line, KnuthNode node) { | |||
KnuthNode n = getNode(line); | |||
if (n != node) { | |||
if (bPendingFootnotes) { | |||
// nodes could be rightly deactivated in a different order | |||
KnuthNode prevNode = null; | |||
while (n != node) { | |||
prevNode = n; | |||
n = n.next; | |||
} | |||
prevNode.next = n.next; | |||
if (prevNode.next == null) { | |||
activeLines[line*2+1] = prevNode; | |||
} | |||
} else { | |||
log.error("Should be first"); | |||
} | |||
} else { | |||
activeLines[line*2] = node.next; | |||
if (node.next == null) { | |||
activeLines[line*2+1] = null; | |||
} | |||
while (startLine < endLine && getNode(startLine) == null) { | |||
startLine++; | |||
} | |||
} | |||
activeNodeCount--; | |||
} | |||
public LinkedList getPageBreaks() { | |||
@@ -55,6 +495,17 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { | |||
double ratio = (blockAlignment == org.apache.fop.fo.Constants.EN_JUSTIFY | |||
|| bestActiveNode.adjustRatio < 0) ? bestActiveNode.adjustRatio : 0; | |||
// compute the indexes of the first footnote list and the first element in that list | |||
int firstListIndex = ((KnuthPageNode) bestActiveNode.previous).footnoteListIndex; | |||
int firstElementIndex = ((KnuthPageNode) bestActiveNode.previous).footnoteElementIndex; | |||
if (footnotesList != null | |||
&& firstElementIndex == ((LinkedList) footnotesList.get(firstListIndex)).size() - 1) { | |||
// advance to the next list | |||
firstListIndex ++; | |||
firstElementIndex = 0; | |||
} else { | |||
firstElementIndex ++; | |||
} | |||
// add nodes at the beginning of the list, as they are found | |||
// backwards, from the last one to the first one | |||
@@ -63,7 +514,11 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { | |||
+ " posizione= " + bestActiveNode.position); | |||
} | |||
insertPageBreakAsFirst(new PageBreakPosition(this.topLevelLM, | |||
bestActiveNode.position, ratio, difference)); | |||
bestActiveNode.position, | |||
firstListIndex, firstElementIndex, | |||
((KnuthPageNode) bestActiveNode).footnoteListIndex, | |||
((KnuthPageNode) bestActiveNode).footnoteElementIndex, | |||
ratio, difference)); | |||
} | |||
protected int filterActiveNodes() { | |||
@@ -80,4 +535,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { | |||
return bestActiveNode.line; | |||
} | |||
public LinkedList getFootnoteList(int index) { | |||
return (LinkedList) footnotesList.get(index); | |||
} | |||
} |
@@ -22,6 +22,8 @@ import org.apache.fop.apps.FOPException; | |||
import org.apache.fop.area.AreaTreeHandler; | |||
import org.apache.fop.area.AreaTreeModel; | |||
import org.apache.fop.area.Block; | |||
import org.apache.fop.area.Footnote; | |||
import org.apache.fop.area.PageViewport; | |||
import org.apache.fop.area.LineArea; | |||
import org.apache.fop.area.Resolvable; | |||
@@ -37,8 +39,11 @@ import org.apache.fop.fo.pagination.SideRegion; | |||
import org.apache.fop.fo.pagination.SimplePageMaster; | |||
import org.apache.fop.fo.pagination.StaticContent; | |||
import org.apache.fop.traits.MinOptMax; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import java.util.ListIterator; | |||
/** | |||
* LayoutManager for a PageSequence. This class is instantiated by | |||
@@ -80,6 +85,8 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { | |||
private int startPageNum = 0; | |||
private int currentPageNum = 0; | |||
private Block separatorArea = null; | |||
/** | |||
* Constructor | |||
* | |||
@@ -137,6 +144,9 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { | |||
private PageSequenceLayoutManager pslm; | |||
private boolean firstPart = true; | |||
private StaticContentLayoutManager footnoteSeparatorLM = null; | |||
private LinkedList footnoteSeparatorList = null; | |||
public PageBreaker(PageSequenceLayoutManager pslm) { | |||
this.pslm = pslm; | |||
} | |||
@@ -153,7 +163,67 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { | |||
} | |||
protected LinkedList getNextKnuthElements(LayoutContext context, int alignment) { | |||
return pslm.getNextKnuthElements(context, alignment); | |||
LinkedList contentList = pslm.getNextKnuthElements(context, alignment); | |||
// scan contentList, searching for footnotes | |||
boolean bFootnotesPresent = false; | |||
if (contentList != null) { | |||
ListIterator contentListIterator = contentList.listIterator(); | |||
while (contentListIterator.hasNext()) { | |||
KnuthElement element = (KnuthElement) contentListIterator.next(); | |||
if (element instanceof KnuthBlockBox | |||
&& ((KnuthBlockBox) element).hasAnchors()) { | |||
// element represents a line with footnote citations | |||
bFootnotesPresent = true; | |||
LinkedList footnoteBodyLMs = ((KnuthBlockBox) element).getFootnoteBodyLMs(); | |||
ListIterator footnoteBodyIterator = footnoteBodyLMs.listIterator(); | |||
// store the lists of elements representing the footnote bodies | |||
// in the box representing the line containing their references | |||
while (footnoteBodyIterator.hasNext()) { | |||
FootnoteBodyLayoutManager fblm = (FootnoteBodyLayoutManager) footnoteBodyIterator.next(); | |||
fblm.setParent(childFLM); | |||
((KnuthBlockBox) element).addElementList(fblm.getNextKnuthElements(context, alignment)); | |||
} | |||
} | |||
} | |||
} | |||
// handle the footnote separator | |||
StaticContent footnoteSeparator; | |||
if (bFootnotesPresent | |||
&& (footnoteSeparator = pageSeq.getStaticContent("xsl-footnote-separator")) != null) { | |||
// create a Block area that will contain the separator areas | |||
separatorArea = new Block(); | |||
// create a StaticContentLM for the footnote separator | |||
footnoteSeparatorLM = (StaticContentLayoutManager) | |||
getLayoutManagerMaker().makeStaticContentLayoutManager( | |||
pslm, footnoteSeparator, separatorArea); | |||
// get the list of elements representing the footnote separator | |||
footnoteSeparatorList = footnoteSeparatorLM.getNextKnuthElements(context, alignment); | |||
// compute the total length of the elements | |||
footnoteSeparatorLength = new MinOptMax(0); | |||
ListIterator separatorIterator = footnoteSeparatorList.listIterator(); | |||
while (separatorIterator.hasNext()) { | |||
KnuthElement element = (KnuthElement) separatorIterator.next(); | |||
if (element.isBox()) { | |||
footnoteSeparatorLength.add(new MinOptMax(element.getW())); | |||
} else if (element.isGlue()) { | |||
footnoteSeparatorLength.add(new MinOptMax(element.getW())); | |||
footnoteSeparatorLength.max += element.getY(); | |||
footnoteSeparatorLength.min -= element.getZ(); | |||
} | |||
} | |||
// add the footnote separator areas to the Block area | |||
if (footnoteSeparatorList != null) { | |||
LayoutContext childLC = new LayoutContext(0); | |||
footnoteSeparatorLM.addAreas(new KnuthPossPosIter(footnoteSeparatorList, 0, footnoteSeparatorList.size()), childLC); | |||
} | |||
} | |||
return contentList; | |||
} | |||
protected int getCurrentDisplayAlign() { | |||
@@ -198,7 +268,30 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { | |||
firstPart = false; | |||
} | |||
protected void finishPart() { | |||
protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) { | |||
// add footnote areas | |||
if (pbp.footnoteFirstListIndex < pbp.footnoteLastListIndex | |||
|| pbp.footnoteFirstElementIndex <= pbp.footnoteLastElementIndex) { | |||
// call addAreas() for each FootnoteBodyLM | |||
for (int i = pbp.footnoteFirstListIndex; i <= pbp.footnoteLastListIndex; i++) { | |||
LinkedList elementList = alg.getFootnoteList(i); | |||
int firstIndex = (i == pbp.footnoteFirstListIndex ? pbp.footnoteFirstElementIndex : 0); | |||
int lastIndex = (i == pbp.footnoteLastListIndex ? pbp.footnoteLastElementIndex : elementList.size() - 1); | |||
FootnoteBodyLayoutManager fblm = (FootnoteBodyLayoutManager) | |||
((KnuthElement) elementList.getFirst()).getLayoutManager(); | |||
LayoutContext childLC = new LayoutContext(0); | |||
fblm.addAreas(new KnuthPossPosIter(elementList, firstIndex, lastIndex + 1), childLC); | |||
} | |||
// set the offset from the top margin | |||
Footnote parentArea = (Footnote) getCurrentPV().getBodyRegion().getFootnote(); | |||
int topOffset = (int) curPV.getBodyRegion().getBPD() - parentArea.getBPD(); | |||
if (separatorArea != null) { | |||
topOffset -= separatorArea.getBPD(); | |||
} | |||
parentArea.setTop(topOffset); | |||
parentArea.setSeparator(separatorArea); | |||
} | |||
} | |||
protected LayoutManager getCurrentChildLM() { | |||
@@ -225,14 +318,14 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { | |||
LayoutManager curLM; // currently active LM | |||
while ((curLM = getChildLM()) != null) { | |||
/*LF*/ LinkedList returnedList = null; | |||
/*LF*/ if (childFLM == null && (curLM instanceof FlowLayoutManager)) { | |||
/*LF*/ childFLM = (FlowLayoutManager)curLM; | |||
/*LF*/ } else { | |||
/*LF*/ if (curLM != childFLM) { | |||
/*LF*/ log.error("PSLM> invalid child LM"); | |||
/*LF*/ } | |||
/*LF*/ } | |||
LinkedList returnedList = null; | |||
if (childFLM == null && (curLM instanceof FlowLayoutManager)) { | |||
childFLM = (FlowLayoutManager)curLM; | |||
} else { | |||
if (curLM != childFLM) { | |||
log.error("PSLM> invalid child LM"); | |||
} | |||
} | |||
LayoutContext childLC = new LayoutContext(0); | |||
childLC.setStackLimit(context.getStackLimit()); | |||
@@ -243,7 +336,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { | |||
int flowBPD = (int) curPV.getBodyRegion().getBPD(); | |||
pageSeq.setLayoutDimension(PercentBase.REFERENCE_AREA_IPD, flowIPD); | |||
pageSeq.setLayoutDimension(PercentBase.REFERENCE_AREA_BPD, flowBPD); | |||
/*LF*/ returnedList = curLM.getNextKnuthElements(childLC, alignment); | |||
returnedList = curLM.getNextKnuthElements(childLC, alignment); | |||
} | |||
if (returnedList != null) { | |||
return returnedList; |
@@ -39,6 +39,7 @@ import java.util.ListIterator; | |||
*/ | |||
public class StaticContentLayoutManager extends BlockStackingLayoutManager { | |||
private RegionReference targetRegion; | |||
private Block targetBlock; | |||
private List blockBreaks = new ArrayList(); | |||
private SideRegion regionFO; | |||
@@ -50,6 +51,13 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager { | |||
targetRegion = getCurrentPV().getRegionReference(regionFO.getNameId()); | |||
} | |||
public StaticContentLayoutManager(PageSequenceLayoutManager pslm, | |||
StaticContent node, Block block) { | |||
super(node); | |||
setParent(pslm); | |||
targetBlock = block; | |||
} | |||
/** | |||
* @see org.apache.fop.layoutmgr.LayoutManager#getNextKnuthElements(org.apache.fop.layoutmgr.LayoutContext, int) | |||
*/ | |||
@@ -206,15 +214,23 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager { | |||
* @see org.apache.fop.layoutmgr.LayoutManager#addChildArea(Area) | |||
*/ | |||
public void addChildArea(Area childArea) { | |||
if (getStaticContentFO().getFlowName().equals("xsl-footnote-separator")) { | |||
targetBlock.addBlock((Block)childArea); | |||
} else { | |||
targetRegion.addBlock((Block)childArea); | |||
} | |||
} | |||
/** | |||
* @see org.apache.fop.layoutmgr.LayoutManager#getParentArea(Area) | |||
*/ | |||
public Area getParentArea(Area childArea) { | |||
if (getStaticContentFO().getFlowName().equals("xsl-footnote-separator")) { | |||
return targetBlock; | |||
} else { | |||
return targetRegion; | |||
} | |||
} | |||
public void doLayout() { | |||
StaticContentBreaker breaker = new StaticContentBreaker( | |||
@@ -228,6 +244,13 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager { | |||
} | |||
} | |||
/** | |||
* convenience method that returns the Static Content node | |||
*/ | |||
protected StaticContent getStaticContentFO() { | |||
return (StaticContent) fobj; | |||
} | |||
private class StaticContentBreaker extends AbstractBreaker { | |||
private StaticContentLayoutManager lm; | |||
private int displayAlign; | |||
@@ -298,7 +321,7 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager { | |||
} | |||
} | |||
protected void finishPart() { | |||
protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) { | |||
//nop for static content | |||
} | |||
@@ -348,6 +348,7 @@ public abstract class AbstractRenderer | |||
* @param footnote The footnote | |||
*/ | |||
protected void renderFootnote(Footnote footnote) { | |||
currentBPPosition += footnote.getTop(); | |||
List blocks = footnote.getChildAreas(); | |||
if (blocks != null) { | |||
Block sep = footnote.getSeparator(); |