]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Footnote implementation: changes to existing files
authorLuca Furini <lfurini@apache.org>
Tue, 17 May 2005 14:30:33 +0000 (14:30 +0000)
committerLuca Furini <lfurini@apache.org>
Tue, 17 May 2005 14:30:33 +0000 (14:30 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@198637 13f79535-47bb-0310-9956-ffa450edef68

17 files changed:
src/java/org/apache/fop/area/Footnote.java
src/java/org/apache/fop/fo/flow/Footnote.java
src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java
src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java
src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java
src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java
src/java/org/apache/fop/layoutmgr/InlineStackingLayoutManager.java
src/java/org/apache/fop/layoutmgr/KnuthBlockBox.java
src/java/org/apache/fop/layoutmgr/KnuthInlineBox.java
src/java/org/apache/fop/layoutmgr/LayoutManagerMaker.java
src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java
src/java/org/apache/fop/layoutmgr/LineLayoutManager.java
src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java
src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java
src/java/org/apache/fop/render/AbstractRenderer.java

index e1b0c453e9935372540599466e8b7baab391500e..edcff170c6b9ce701ea2f2b317df023802062bfb 100644 (file)
@@ -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;
+    }
 }
 
index 2d161c2c85ff7e0f0eaf83ae47066bcde3f0a787..7c7b1a8f854e3ebd9881f8aaefd3b0cdd8d0051b 100644 (file)
@@ -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;
     }
 
     /**
index e170616e0e35134a09d17b795102dba1007ab518..73dd8678c40700eef3fbbbc58c24a59492210662 100644 (file)
@@ -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;
         }
index 1fda2cfc483137209c588fd744f7e95e36950315..76ed74224c867c81184043e73c84f1b785d16f7f 100644 (file)
@@ -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
         }
         
index 05abe663f70acaf53db0bf9a7f4f6b0fc9cc9fa0..82faa794bdbf8dd574408327a10c051446c25aa7 100644 (file)
@@ -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);
             }
 
index bd947febc6eb8721c853be921ab1c9f5d7646797..0aa581b7733967b26b93d3479284bbadfdc9a047 100644 (file)
@@ -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);
     }
 
index 08b29e21181289623b3ac915cf6f6fb88e6819c8..3c1f090cb4d10e560503cdbe8a91db79814dc550 100644 (file)
@@ -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;
     }
index 1993145769e427e04f1015157811019ba49774dd..fbff4ff0f590777eb670199da91abe8b0c846370 100644 (file)
@@ -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;
index 896532ebb8573057815948a8c76f8a37cd82df37..671cfecab6fdae18523702c0ae36b42f076fa260 100644 (file)
@@ -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() {
index cca2bbbb5e0165fbe3c79f08cad932be021ee2ab..ded7e65935f3bf5e6afe98aa40fbcd0c73a9cf60 100644 (file)
@@ -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
index 38ddc1e57ac123077dc2288925fe1a6a2eed144c..bfd08599f5d2c09b006e031137b17686684cf4fe 100644 (file)
@@ -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);
+
 }
 
index d47cd059fa72e0e97be100c7e2f05938884427a1..b3bb1c438717de0e6d1fc6447a8c41f9de3d6e67 100644 (file)
@@ -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));
         }
     }
 
index 0f72ef124c964cd585cab072936be41c0394c04f..83da8ba221fe1fe6acc67f79b5536f35c4e5d69f 100644 (file)
@@ -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));
                 }
             }
         }
index 6ca570d3b2a18eb0e5e9aa00e45cf28095e0f2a7..c2df28b178ddffeda812736b84022d369aad8d00 100644 (file)
 
 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>&gt;= -1 && &lt; 0  means that the break is to wider than the line, 
+     *        but within the minimim values of the glues.</li> 
+     *    <li>&gt;0 && &lt 1 means that the break is smaller than the line width, 
+     *        but within the maximum values of the glues.</li>
+     *    <li>&gt 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
index 64b35f0c61c79bfc436b5baca4f813b68aaa9e91..d5363f2c6f45ead3c4bcbaae1a93cb9ef754e6c5 100644 (file)
@@ -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;
index 828f2bf6a1003db2a2af9e6ed4694527c24ab191..e305ff48ea8879a33f3fea9652ffae63c2e62bc7 100644 (file)
@@ -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
         }
         
index 2bbd89fadd2b8bd1016c665bad3e8d50f4bd0b3d..ea7ce4da180a575066cb113f26802373443aac6f 100644 (file)
@@ -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();