diff options
17 files changed, 812 insertions, 96 deletions
diff --git a/src/java/org/apache/fop/area/Footnote.java b/src/java/org/apache/fop/area/Footnote.java index e1b0c453e..edcff170c 100644 --- a/src/java/org/apache/fop/area/Footnote.java +++ b/src/java/org/apache/fop/area/Footnote.java @@ -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; + } } diff --git a/src/java/org/apache/fop/fo/flow/Footnote.java b/src/java/org/apache/fop/fo/flow/Footnote.java index 2d161c2c8..7c7b1a8f8 100644 --- a/src/java/org/apache/fop/fo/flow/Footnote.java +++ b/src/java/org/apache/fop/fo/flow/Footnote.java @@ -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; } /** diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java index e170616e0..73dd8678c 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java @@ -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; } diff --git a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java index 1fda2cfc4..76ed74224 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java @@ -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 } diff --git a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java index 05abe663f..82faa794b 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java @@ -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); } diff --git a/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java index bd947febc..0aa581b77 100644 --- a/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java +++ b/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java @@ -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); } diff --git a/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java b/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java index 08b29e211..3c1f090cb 100644 --- a/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java @@ -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; } diff --git a/src/java/org/apache/fop/layoutmgr/InlineStackingLayoutManager.java b/src/java/org/apache/fop/layoutmgr/InlineStackingLayoutManager.java index 199314576..fbff4ff0f 100644 --- a/src/java/org/apache/fop/layoutmgr/InlineStackingLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/InlineStackingLayoutManager.java @@ -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; diff --git a/src/java/org/apache/fop/layoutmgr/KnuthBlockBox.java b/src/java/org/apache/fop/layoutmgr/KnuthBlockBox.java index 896532ebb..671cfecab 100644 --- a/src/java/org/apache/fop/layoutmgr/KnuthBlockBox.java +++ b/src/java/org/apache/fop/layoutmgr/KnuthBlockBox.java @@ -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() { diff --git a/src/java/org/apache/fop/layoutmgr/KnuthInlineBox.java b/src/java/org/apache/fop/layoutmgr/KnuthInlineBox.java index cca2bbbb5..ded7e6593 100644 --- a/src/java/org/apache/fop/layoutmgr/KnuthInlineBox.java +++ b/src/java/org/apache/fop/layoutmgr/KnuthInlineBox.java @@ -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); + } }
\ No newline at end of file diff --git a/src/java/org/apache/fop/layoutmgr/LayoutManagerMaker.java b/src/java/org/apache/fop/layoutmgr/LayoutManagerMaker.java index 38ddc1e57..bfd08599f 100644 --- a/src/java/org/apache/fop/layoutmgr/LayoutManagerMaker.java +++ b/src/java/org/apache/fop/layoutmgr/LayoutManagerMaker.java @@ -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); + } diff --git a/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java b/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java index d47cd059f..b3bb1c438 100644 --- a/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java +++ b/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java @@ -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)); } } diff --git a/src/java/org/apache/fop/layoutmgr/LineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/LineLayoutManager.java index 0f72ef124..83da8ba22 100644 --- a/src/java/org/apache/fop/layoutmgr/LineLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/LineLayoutManager.java @@ -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)); } } } diff --git a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java index 6ca570d3b..c2df28b17 100644 --- a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java +++ b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java @@ -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); + } }
\ No newline at end of file diff --git a/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java b/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java index 64b35f0c6..d5363f2c6 100644 --- a/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java @@ -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; diff --git a/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java index 828f2bf6a..e305ff48e 100644 --- a/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java @@ -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 } diff --git a/src/java/org/apache/fop/render/AbstractRenderer.java b/src/java/org/apache/fop/render/AbstractRenderer.java index 2bbd89fad..ea7ce4da1 100644 --- a/src/java/org/apache/fop/render/AbstractRenderer.java +++ b/src/java/org/apache/fop/render/AbstractRenderer.java @@ -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(); |