Index: src/java/org/apache/fop/layoutmgr/breaking/OutOfLineRecord.java =================================================================== --- src/java/org/apache/fop/layoutmgr/breaking/OutOfLineRecord.java (révision 0) +++ src/java/org/apache/fop/layoutmgr/breaking/OutOfLineRecord.java (révision 0) @@ -0,0 +1,600 @@ +/* + * Copyright 2004-2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.layoutmgr.breaking; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; + +import org.apache.fop.layoutmgr.KnuthElement; +import org.apache.fop.layoutmgr.PageBreakingAlgorithm; +import org.apache.fop.layoutmgr.SpaceResolver; +import org.apache.fop.layoutmgr.PageBreakingAlgorithm.KnuthPageNode; +import org.apache.fop.traits.MinOptMax; + +/** + * Helper class for dealing with out-of-line objects (before-floats and footnotes) when + * breaking text into pages. It stores the necessary informations to place out-of-line + * objects, and provides methods to manipulate them. + * + * @see PageBreakingAlgorithm + */ +public class OutOfLineRecord { + + /** + * Stores informations about how many out-of-line objects have already been handled. + */ + public static class ProgressInfo { + + /** Cumulated BPD length of all out-of-line objects inserted so far. */ + private int alreadyInsertedLength = 0; + + /** Index of the last inserted out-of-line object. */ + private int lastInsertedIndex = -1; + + /** + * Index of the last inserted Knuth element of the last inserted out-of-line + * object. Currently only used for footnotes, as before-floats may not be split on + * several pages. Might be useful when later dealing with floats that cannot even + * be put on a page alone, however. + */ + private int lastElementOfLastInsertedIndex = -1; + + /** + * Initializes this record, as if no out-of-line object were handled yet. + */ + private void initialize() { + alreadyInsertedLength = 0; + lastInsertedIndex = -1; + lastElementOfLastInsertedIndex = -1; + } + + /** + * @return a copy of this record + */ + public ProgressInfo copy() { + ProgressInfo info = new ProgressInfo(); + info.alreadyInsertedLength = alreadyInsertedLength; + info.lastInsertedIndex = lastInsertedIndex; + info.lastElementOfLastInsertedIndex = lastElementOfLastInsertedIndex; + return info; + } + + /** + * Returns the cumulated length of all already typeset out-of-line objects. + * @return the total length in the block-progression-direction + */ + public int getAlreadyInsertedLength() { + return alreadyInsertedLength; + } + + /** + * Returns the index of the last element of the last already typeset out-of-line + * object. + * @return the index of the last placed KnuthElement + */ + public int getLastElementOfLastInsertedIndex() { + return lastElementOfLastInsertedIndex; + } + + /** + * @return the index of the last already typeset out-of-line object. + */ + public int getLastInsertedIndex() { + return lastInsertedIndex; + } + + public String toString() { + return "length=" + alreadyInsertedLength + + ", index=" + lastInsertedIndex + + ", elt=" + lastElementOfLastInsertedIndex; + } + } + + /** + * Sequences of KnuthElement corresponding to already encountered out-of-line objects. + * This is a List of List of KnuthElement. + */ + private List knuthSequences = null; + + /** + * Each element of this list corresponds to the cumulated length in the BPD of all the + * out-of-line objects up to the given index. This is a List of Integer. + * + * @see OutOfLineRecord#knuthSequences + */ + private List cumulativeLengths = null; + + /** + * True if new out-of-line objects are cited in the sequence of Knuth elements since + * the last encountered legal breakpoint. + * + * @see OutOfLineRecord#newSinceLastBreakpoint() + */ + private boolean newSinceLastBreakpoint = false; + + /** + * Index of the first newly encountered out-of-line object since the last legal + * breakpoint. + * + * @see OutOfLineRecord#knuthSequences + */ + private int firstNewIndex = 0; + + /** + * Dimension in the BPD of the separator between the out-of-line area and the main + * area. + */ + private MinOptMax separatorLength = null; + + /** + * Record of already handled out-of-line objects. + * + * @see ProgressInfo + */ + private ProgressInfo progressInfo; + + public OutOfLineRecord(MinOptMax separatorLength) { + this.separatorLength = separatorLength; + this.progressInfo = new ProgressInfo(); + } + + /** + * Initializes this record, as if no out-of-line object were handled yet. + */ + public void initialize() { + knuthSequences = null; + cumulativeLengths = null; + newSinceLastBreakpoint = false; + firstNewIndex = 0; + progressInfo.initialize(); + } + + /** + * @return the informations about already handled out-of-line objects + */ + public ProgressInfo getProgress() { + return this.progressInfo; + } + + /** + * @return the length in the BPD of the separator between the out-of-line area and the + * main area. + */ + public MinOptMax getSeparatorLength() { + return separatorLength; + } + + /** + * @return the total length of already encountered out-of-line objects + */ + public int getTotalLength() { + if (cumulativeLengths == null || cumulativeLengths.size() == 0) { + return 0; + } else { + return ((Integer) cumulativeLengths.get(cumulativeLengths.size() - 1)).intValue(); + } + } + + /** + * @return true if out-of-line objects have already been encountered (but not + * necessarily typeset yet) + */ + public boolean existing() { + return (knuthSequences != null && knuthSequences.size() > 0); + } + + public void resetNewSinceLastBreakpoint() { + newSinceLastBreakpoint = false; + } + + /** + * @return true if new out-of-line objects are cited in the sequence of Knuth + * elements since the last encountered legal breakpoint. + */ + public boolean newSinceLastBreakpoint() { + return newSinceLastBreakpoint; + } + + /** + * Records one or more newly encountered out-of-line objects. + * @param elementLists the list of corresponding Knuth sequences + */ + public void add(List elementLists) { + // Initialize stuff if necessary + if (knuthSequences == null) { + knuthSequences = new ArrayList(); + cumulativeLengths = new ArrayList(); + } + if (!newSinceLastBreakpoint) { + newSinceLastBreakpoint = true; + firstNewIndex = knuthSequences.size(); + } + // compute the total length of the footnotes + ListIterator elementListsIterator = elementLists.listIterator(); + while (elementListsIterator.hasNext()) { + LinkedList noteList = (LinkedList) elementListsIterator.next(); + + //Space resolution (Note: this does not respect possible stacking constraints + //between footnotes!) + SpaceResolver.resolveElementList(noteList); + + int noteLength = 0; + knuthSequences.add(noteList); + ListIterator noteListIterator = noteList.listIterator(); + while (noteListIterator.hasNext()) { + KnuthElement element = (KnuthElement) noteListIterator.next(); + if (element.isBox() || element.isGlue()) { + noteLength += element.getW(); + } + } + cumulativeLengths.add(new Integer(getTotalLength() + noteLength)); + } + } + + /** + * Sets the progress informations to the given values. Called whenever a new active + * node is considered; the informations regarding already handled out-of-line objects + * must be set to the active node's values in order to know from where to start the + * placement of further objects. + * + * @param info progress informations of the currently considered active node + */ + public void setProgress(ProgressInfo info) { + this.progressInfo.alreadyInsertedLength = info.alreadyInsertedLength; + this.progressInfo.lastElementOfLastInsertedIndex = info.lastElementOfLastInsertedIndex; + this.progressInfo.lastInsertedIndex = info.lastInsertedIndex; + } + + /* Unless I'm wrong, newOnThisPagePlusPiecesFromPrevious always implies + * notAllInserted. And if A => B, then A && B <=> B + * So this code may be simplified, see deferred() below + */ + /** +// * Returns true if their are (pieces of) footnotes to be typeset on the +// * current page. +// * @param listIndex index of the last inserted footnote for the +// * currently considered active node +// * @param elementIndex index of the last element of the last inserted footnote +// * @param length total length of all footnotes inserted so far +// */ +// public boolean deferredFootnotes(ProgressInfo progressInfo) { +// boolean newOnThisPagePlusPiecesFromPrevious = +// newSinceLastBreakpoint() +// && firstNewIndex != 0 +// && (progressInfo.lastInsertedIndex < firstNewIndex - 1 +// || progressInfo.lastElementOfLastInsertedIndex < +// ((LinkedList) knuthSequences.get(progressInfo.lastInsertedIndex)).size() - 1); +// boolean notAllInserted = progressInfo.alreadyInsertedLength < getTotalLength(); +// return notAllInserted; +// } + + /** + * @return true if some out-of-line objects have not already been + * typeset. + */ + public boolean deferred() { + return progressInfo.alreadyInsertedLength < getTotalLength(); + } + + /** + * @return the number of not yet typeset out-of-line objects. + */ + public int getNbOfDeferred() { + return knuthSequences.size() - 1 - progressInfo.lastInsertedIndex; + } + + /** + * @return true if the last typeset out-of-line object must be split on + * several pages. + */ + public boolean isSplit() { + return (progressInfo.lastElementOfLastInsertedIndex + < ((LinkedList) knuthSequences.get(progressInfo.lastInsertedIndex)).size() - 1); + } + + /** + * Returns the out-of-line object corresponding to the given index. + * @param index index of the object + * @return a List of KnuthElement corresponding to the object, or null if + * it does not exist + */ + public List getSequence(int index) { + /*TODO vh: bof */ + if (knuthSequences == null) { + return null; + } else { + return (List) knuthSequences.get(index); + } + } + + /** + * Tries to split the flow of footnotes to put one part on the current page. + * @param prevNodeProgress informations about footnotes already inserted on the + * previous page + * @param availableLength available space for footnotes on this page + * @param canDeferOldFootnotes + * @return the length of footnotes which could be inserted on this page + */ + public int getFootnoteSplit(ProgressInfo prevNodeProgress, + int availableLength, boolean canDeferOldFootnotes) { + if (availableLength <= 0) { + progressInfo.alreadyInsertedLength = prevNodeProgress.getAlreadyInsertedLength(); + return 0; + } else { + // the split should contain a piece of the last footnote + // together with all previous, not yet inserted footnotes; + // but if this is not possible, try adding as much content as possible + int splitLength = 0; + ListIterator noteListIterator = null; + KnuthElement element = null; + boolean somethingAdded = false; + + // prevNodeProgress.lastInsertedIndex and + // prevNodeProgress.lastElementOfLastInsertedIndex points to the last footnote element + // already placed in a page: advance to the next element + int listIndex = prevNodeProgress.lastInsertedIndex; + int elementIndex = prevNodeProgress.lastElementOfLastInsertedIndex; + if (listIndex == -1 + || elementIndex == ((LinkedList) knuthSequences.get(listIndex)).size() - 1) { + listIndex++; + elementIndex = 0; + } else { + elementIndex++; + } + + // try adding whole notes + // if there are more than 1 footnote to insert + if (knuthSequences.size() - 1 > listIndex) { + // add the previous footnotes: these cannot be broken or deferred + if (!canDeferOldFootnotes + && newSinceLastBreakpoint() + && firstNewIndex > 0) { + splitLength = ((Integer) cumulativeLengths.get(firstNewIndex - 1)).intValue() + - prevNodeProgress.alreadyInsertedLength; + listIndex = firstNewIndex; + elementIndex = 0; + } + // try adding the new footnotes + while (((Integer) cumulativeLengths.get(listIndex)).intValue() + - prevNodeProgress.alreadyInsertedLength <= availableLength) { + splitLength = ((Integer) cumulativeLengths.get(listIndex)).intValue() + - prevNodeProgress.alreadyInsertedLength; + somethingAdded = true; + listIndex++; + elementIndex = 0; + } + // as this method is called only if it is not possible to insert + // all footnotes, at this point listIndex and elementIndex points to + // an existing element, the next one we will try to insert + } + + // try adding a split of the next note + noteListIterator = ((List) knuthSequences.get(listIndex)).listIterator(elementIndex); + + int prevSplitLength = 0; + int prevIndex = -1; + int index = -1; + + while (!somethingAdded || splitLength <= availableLength) { + if (!somethingAdded) { + somethingAdded = true; + } else { + prevSplitLength = splitLength; + prevIndex = index; + } + // get a sub-sequence from the note element list + boolean bPrevIsBox = false; + while (noteListIterator.hasNext()) { + // as this method is called only if it is not possible to insert + // all footnotes, and we have already tried (and failed) to insert + // this whole footnote, the while loop will never reach the end + // of the note sequence + 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) { + // end of the sub-sequence + 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 (!somethingAdded) { + // there was not enough space to add a piece of the first new footnote + // this is not a good break + prevSplitLength = 0; + } else if (prevSplitLength > 0) { + // prevIndex is -1 if we have added only some whole footnotes + progressInfo.lastInsertedIndex = (prevIndex != -1) ? listIndex : listIndex - 1; + progressInfo.lastElementOfLastInsertedIndex = (prevIndex != -1) + ? prevIndex + : ((LinkedList) knuthSequences.get(progressInfo.lastInsertedIndex)).size() - 1; + } + progressInfo.alreadyInsertedLength + = prevNodeProgress.getAlreadyInsertedLength() + prevSplitLength; + return prevSplitLength; + } + } + + /** + * Tries to split the flow of floats to put some floats on the current page. + * @param prevProgress floats already inserted on the previous page + * @param availableLength available space for floats + * @return the length of floats which could be placed on the current page + */ + public int getFloatSplit(ProgressInfo prevProgress, int availableLength) { + /* + * Normally this method is called only when there is some place for + * floats => availableLength > 0 + */ + int splitLength = 0; + int listIndex = prevProgress.lastInsertedIndex + 1; + + while (listIndex < knuthSequences.size() + && ((Integer) cumulativeLengths.get(listIndex)).intValue() + - prevProgress.alreadyInsertedLength <= availableLength) { + splitLength = ((Integer) cumulativeLengths.get(listIndex)).intValue() + - prevProgress.alreadyInsertedLength; + listIndex++; + } + progressInfo.lastInsertedIndex = listIndex - 1; + progressInfo.alreadyInsertedLength = prevProgress.alreadyInsertedLength + splitLength; + return splitLength; + } + + /** + * Places on the current page all of the out-of-line objects not yet inserted. + */ + public void insertAll() { + progressInfo.alreadyInsertedLength = getTotalLength(); + progressInfo.lastInsertedIndex = knuthSequences.size() - 1; + progressInfo.lastElementOfLastInsertedIndex + = ((List) knuthSequences.get(progressInfo.lastInsertedIndex)).size() - 1; + } + + /** + * When restarting the algorithm from a given point, reset the informations about + * out-of-line objects to the values at that point. + * @param elementLists out-of-line sequences which are met after the restarting point, + * and thus must be removed from the list of already encoutered objects. + */ + public void reset(List elementLists) { + for (int i = 0; i < elementLists.size(); i++) { + knuthSequences.remove(knuthSequences.size() - 1); + cumulativeLengths.remove(cumulativeLengths.size() - 1); + } + } + + /** + * When the whole normal flow has been typeset and there are still footnotes to be + * placed, creates as many pages as necessary to place them. + */ + public void createFootnotePages(KnuthPageNode lastNode, PageBreakingAlgorithm algo, int lineWidth) { + progressInfo.alreadyInsertedLength = lastNode.footnotesProgress.getAlreadyInsertedLength(); + progressInfo.lastInsertedIndex = lastNode.footnotesProgress.getLastInsertedIndex(); + progressInfo.lastElementOfLastInsertedIndex = lastNode.footnotesProgress.getLastElementOfLastInsertedIndex(); + int availableBPD = lineWidth; + int split = 0; + KnuthPageNode prevNode = lastNode; + + // create pages containing the remaining footnote bodies + while (progressInfo.alreadyInsertedLength < getTotalLength()) { + // try adding some more content + if (((Integer) cumulativeLengths.get(progressInfo.lastInsertedIndex)).intValue() - progressInfo.alreadyInsertedLength + <= availableBPD) { + // add a whole footnote + availableBPD -= ((Integer) cumulativeLengths.get(progressInfo.lastInsertedIndex)).intValue() + - progressInfo.alreadyInsertedLength; + progressInfo.alreadyInsertedLength = ((Integer)cumulativeLengths.get(progressInfo.lastInsertedIndex)).intValue(); + progressInfo.lastElementOfLastInsertedIndex + = ((LinkedList)knuthSequences.get(progressInfo.lastInsertedIndex)).size() - 1; + } else if ((split = getFootnoteSplit(progressInfo, availableBPD, true)) + > 0) { + // add a piece of a footnote + availableBPD -= split; + // footnoteListIndex has already been set in getFootnoteSplit() + // footnoteElementIndex has already been set in getFootnoteSplit() + } else { + // cannot add any content: create a new node and start again + KnuthPageNode node = (KnuthPageNode) + algo.createNode(lastNode.position, prevNode.line + 1, 1, + progressInfo.alreadyInsertedLength - prevNode.footnotesProgress.getAlreadyInsertedLength(), + 0, 0, + 0, 0, 0, + 0, 0, prevNode); + algo.addNode(node.line, node); + algo.removeNode(prevNode.line, prevNode); + + prevNode = node; + availableBPD = lineWidth; + } + } + // create the last node + KnuthPageNode node = (KnuthPageNode) + algo.createNode(lastNode.position, prevNode.line + 1, 1, + getTotalLength() - prevNode.footnotesProgress.getAlreadyInsertedLength(), 0, 0, + 0, 0, 0, + 0, 0, prevNode); + algo.addNode(node.line, node); + algo.removeNode(prevNode.line, prevNode); + } + + /* TODO vh: won't work when there are also footnotes. To be merged with createFootnotePages */ + public void createFloatPages(KnuthPageNode lastNode, PageBreakingAlgorithm algo, int lineWidth) { + progressInfo.alreadyInsertedLength = lastNode.floatsProgress.getAlreadyInsertedLength(); + progressInfo.lastInsertedIndex = lastNode.floatsProgress.getLastInsertedIndex(); + int availableBPD = lineWidth; + KnuthPageNode prevNode = lastNode; + + // create pages containing the remaining float bodies + while (progressInfo.alreadyInsertedLength < getTotalLength()) { + // try adding some more content + if (((Integer) cumulativeLengths.get(progressInfo.lastInsertedIndex + 1)).intValue() - progressInfo.alreadyInsertedLength + <= availableBPD) { + // add a whole float + progressInfo.lastInsertedIndex++; + availableBPD -= ((Integer) cumulativeLengths.get(progressInfo.lastInsertedIndex)).intValue() + - progressInfo.alreadyInsertedLength; + progressInfo.alreadyInsertedLength = ((Integer)cumulativeLengths.get(progressInfo.lastInsertedIndex)).intValue(); + } else { + // cannot add any content: create a new node and start again + KnuthPageNode node = (KnuthPageNode) + algo.createNode(lastNode.position, prevNode.line + 1, 1, + progressInfo.alreadyInsertedLength - prevNode.floatsProgress.getAlreadyInsertedLength(), + 0, 0, + 0, 0, 0, + 0, prevNode.totalDemerits + (progressInfo.lastInsertedIndex - prevNode.floatsProgress.lastInsertedIndex) * 10000, prevNode); + algo.addNode(node.line, node); + algo.removeNode(prevNode.line, prevNode); + + prevNode = node; + availableBPD = lineWidth; + } + } + // create the last node + KnuthPageNode node = (KnuthPageNode) + algo.createNode(lastNode.position, prevNode.line + 1, 1, + getTotalLength() - prevNode.floatsProgress.getAlreadyInsertedLength(), 0, 0, + 0, 0, 0, + 0, prevNode.totalDemerits + (progressInfo.lastInsertedIndex - prevNode.floatsProgress.lastInsertedIndex) * 10000, prevNode); + algo.addNode(node.line, node); + algo.removeNode(prevNode.line, prevNode); + } +} Index: src/java/org/apache/fop/layoutmgr/FootnoteBodyLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/FootnoteBodyLayoutManager.java (révision 423415) +++ src/java/org/apache/fop/layoutmgr/FootnoteBodyLayoutManager.java (copie de travail) @@ -36,7 +36,7 @@ super(body); } - /** @see org.apache.fop.layoutmgr.LayoutManager */ + /** @see org.apache.fop.layoutmgr.LayoutManager#addAreas(PositionIterator, LayoutContext) */ public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) { LayoutManager childLM = null; LayoutManager lastLM = null; Index: src/java/org/apache/fop/layoutmgr/BalancingColumnBreakingAlgorithm.java =================================================================== --- src/java/org/apache/fop/layoutmgr/BalancingColumnBreakingAlgorithm.java (révision 423415) +++ src/java/org/apache/fop/layoutmgr/BalancingColumnBreakingAlgorithm.java (copie de travail) @@ -38,10 +38,11 @@ PageSequenceLayoutManager.PageProvider pageProvider, int alignment, int alignmentLast, MinOptMax footnoteSeparatorLength, + MinOptMax floatSeparatorLength, boolean partOverflowRecovery, int columnCount) { super(topLevelLM, pageProvider, alignment, alignmentLast, - footnoteSeparatorLength, partOverflowRecovery, false, false); + footnoteSeparatorLength, floatSeparatorLength, partOverflowRecovery, false, false); this.columnCount = columnCount; this.considerTooShort = true; //This is important! } Index: src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java (révision 423415) +++ src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java (copie de travail) @@ -185,7 +185,8 @@ * @see org.apache.fop.layoutmgr.LayoutManager#addChildArea(Area) */ public void addChildArea(Area childArea) { - if (getStaticContentFO().getFlowName().equals("xsl-footnote-separator")) { + if (getStaticContentFO().getFlowName().equals("xsl-footnote-separator") + || getStaticContentFO().getFlowName().equals("xsl-before-float-separator")) { targetBlock.addBlock((Block)childArea); } else { targetRegion.addBlock((Block)childArea); @@ -196,7 +197,8 @@ * @see org.apache.fop.layoutmgr.LayoutManager#getParentArea(Area) */ public Area getParentArea(Area childArea) { - if (getStaticContentFO().getFlowName().equals("xsl-footnote-separator")) { + if (getStaticContentFO().getFlowName().equals("xsl-footnote-separator") + || getStaticContentFO().getFlowName().equals("xsl-before-float-separator")) { return targetBlock; } else { return targetRegion; @@ -213,7 +215,8 @@ boolean autoHeight = false; StaticContentBreaker breaker; - if (getStaticContentFO().getFlowName().equals("xsl-footnote-separator")) { + if (getStaticContentFO().getFlowName().equals("xsl-footnote-separator") + || getStaticContentFO().getFlowName().equals("xsl-before-float-separator")) { targetIPD = targetBlock.getIPD(); targetBPD = targetBlock.getBPD(); if (targetBPD == 0) { Index: src/java/org/apache/fop/layoutmgr/KnuthBlockBox.java =================================================================== --- src/java/org/apache/fop/layoutmgr/KnuthBlockBox.java (révision 423415) +++ src/java/org/apache/fop/layoutmgr/KnuthBlockBox.java (copie de travail) @@ -33,9 +33,20 @@ * it isn't possible to get the opt value stored in a MinOptMax object. */ private int bpd; - private LinkedList footnoteList; - /** List of Knuth elements. This is a list of LinkedList elements. */ - private LinkedList elementLists = null; + /** FootnoteBodyLayoutManagers corresponding to the footnotes cited on this line. */ + private LinkedList footnoteLMList; + /** FloatBodyLayoutManagers corresponding to the floats cited on this line. */ + private LinkedList floatLMList; + /** + * The Knuth sequences corresponding to the footnotes cited on this line. This is a List + * of List of KnuthElement objects. + */ + private LinkedList footnoteElementLists = null; + /** + * The Knuth sequences corresponding to the floats cited on this line. This is a List + * of List of KnuthElement objects. + */ + private LinkedList floatElementLists = null; /** * Creates a new box. @@ -49,59 +60,99 @@ super(w, pos, bAux); ipdRange = (MinOptMax) range.clone(); bpd = bpdim; - footnoteList = new LinkedList(); + footnoteLMList = new LinkedList(); + floatLMList = new LinkedList(); } /** * Creates a new box. * @param w block progression dimension of this box - * @param list footnotes cited by elements in this box. The list contains the - * corresponding FootnoteBodyLayoutManagers + * @param footnoteLMList footnotes cited by elements in this box. The list contains + * the corresponding FootnoteBodyLayoutManagers + * @param floatLMList floats cited by elements in this box. The list contains the + * corresponding FloatBodyLayoutManagers * @param pos the Position stored in this box * @param bAux is this box auxiliary? */ - public KnuthBlockBox(int w, LinkedList list, Position pos, boolean bAux) { + public KnuthBlockBox(int w, + LinkedList footnoteLMList, + LinkedList floatLMList, + Position pos, + boolean bAux) { super(w, pos, bAux); ipdRange = new MinOptMax(0); bpd = 0; - footnoteList = new LinkedList(list); + this.footnoteLMList = new LinkedList(footnoteLMList); + this.floatLMList = new LinkedList(floatLMList); } /** * @return the LMs for the footnotes cited in this box. */ public LinkedList getFootnoteBodyLMs() { - return footnoteList; + return footnoteLMList; } /** + * @return the LMs for the floats cited in this box. + */ + public LinkedList getFloatBodyLMs() { + return floatLMList; + } + + /** * @return true if this box contains footnote citations. */ - public boolean hasAnchors() { - return (footnoteList.size() > 0); + public boolean hasFootnoteAnchors() { + return (footnoteLMList.size() > 0); } /** - * Adds the given list of Knuth elements to this box' list of elements. - * @param list elements corresponding to a footnote body + * @return true if this box contains float citations. */ - public void addElementList(LinkedList list) { - if (elementLists == null) { - elementLists = new LinkedList(); + public boolean hasFloatAnchors() { + return (floatLMList.size() > 0); + } + + /** + * Adds a footnote to this box's list of footnotes. + * @param list KnuthElement instances corresponding to the footnote body + */ + public void addFootnoteElementList(LinkedList list) { + if (footnoteElementLists == null) { + footnoteElementLists = new LinkedList(); } - elementLists.add(list); + footnoteElementLists.add(list); } /** - * Returns the list of Knuth sequences registered by this box. - * @return a list of KnuthElement sequences corresponding to footnotes cited in this - * box + * Returns the list of footnotes cited by this box. + * @return a list of KnuthElement sequences corresponding to the footnote bodies */ - public LinkedList getElementLists() { - return elementLists; + public LinkedList getFootnoteElementLists() { + return footnoteElementLists; } /** + * Adds a float to this box's list of floats. + * @param list KnuthElement instances corresponding to the float body + */ + public void addFloatElementList(LinkedList list) { + if (floatElementLists == null) { + floatElementLists = new LinkedList(); + } + floatElementLists.add(list); + } + + /** + * Returns the list of floats cited by this box. + * @return a list of KnuthElement sequences corresponding to the float bodies + */ + public LinkedList getFloatElementLists() { + return floatElementLists; + } + + /** * @return the inline progression dimension of this box. */ public MinOptMax getIPDRange() { Index: src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java =================================================================== --- src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java (révision 423415) +++ src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java (copie de travail) @@ -27,6 +27,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.fop.fo.Constants; import org.apache.fop.fo.FONode; import org.apache.fop.fo.FOText; import org.apache.fop.fo.FObjMixed; @@ -36,6 +37,7 @@ import org.apache.fop.fo.flow.BlockContainer; import org.apache.fop.fo.flow.Character; import org.apache.fop.fo.flow.ExternalGraphic; +import org.apache.fop.fo.flow.Float; import org.apache.fop.fo.flow.Footnote; import org.apache.fop.fo.flow.Inline; import org.apache.fop.fo.flow.InlineLevel; @@ -68,6 +70,7 @@ import org.apache.fop.layoutmgr.inline.CharacterLayoutManager; import org.apache.fop.layoutmgr.inline.ContentLayoutManager; import org.apache.fop.layoutmgr.inline.ExternalGraphicLayoutManager; +import org.apache.fop.layoutmgr.inline.FloatLayoutManager; import org.apache.fop.layoutmgr.inline.FootnoteLayoutManager; import org.apache.fop.layoutmgr.inline.ICLayoutManager; import org.apache.fop.layoutmgr.inline.InlineLayoutManager; @@ -108,6 +111,7 @@ makers.put(BidiOverride.class, new BidiOverrideLayoutManagerMaker()); makers.put(Inline.class, new InlineLayoutManagerMaker()); makers.put(Footnote.class, new FootnodeLayoutManagerMaker()); + makers.put(Float.class, new FloatLayoutManagerMaker()); makers.put(InlineContainer.class, new InlineContainerLayoutManagerMaker()); makers.put(BasicLink.class, new BasicLinkLayoutManagerMaker()); @@ -268,6 +272,16 @@ } } + public static class FloatLayoutManagerMaker extends Maker { + public void make(FONode node, List lms) { + if (((Float) node).getFloat() == Constants.EN_NONE) { + lms.add(new FloatBodyLayoutManager((Float) node)); + } else { + lms.add(new FloatLayoutManager((Float) node)); + } + } + } + public static class FootnodeLayoutManagerMaker extends Maker { public void make(FONode node, List lms) { lms.add(new FootnoteLayoutManager((Footnote) node)); Index: src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java =================================================================== --- src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java (révision 423415) +++ src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java (copie de travail) @@ -18,9 +18,7 @@ package org.apache.fop.layoutmgr; -import java.util.ArrayList; import java.util.LinkedList; -import java.util.ListIterator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -28,10 +26,11 @@ import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; import org.apache.fop.layoutmgr.AbstractBreaker.PageBreakPosition; +import org.apache.fop.layoutmgr.breaking.OutOfLineRecord; import org.apache.fop.traits.MinOptMax; -class PageBreakingAlgorithm extends BreakingAlgorithm { +public class PageBreakingAlgorithm extends BreakingAlgorithm { /** the logger for the class */ protected static Log classLog = LogFactory.getLog(PageBreakingAlgorithm.class); @@ -41,42 +40,14 @@ /** List of PageBreakPosition elements. */ private LinkedList pageBreaks = null; - /** Footnotes which are cited between the currently considered active node (previous - * break) and the current considered break. Its type is - * List<List<KnuthElement>>, it contains the sequences of KnuthElement - * representing the footnotes bodies. - */ - private ArrayList footnotesList = null; - /** Cumulated bpd of unhandled footnotes. */ - private ArrayList lengthList = null; - /** Length of all the footnotes which will be put on the current page. */ - private int totalFootnotesLength = 0; - /** - * Length of all the footnotes which have already been inserted, up to the currently - * considered element. That is, footnotes from the currently considered page plus - * footnotes from its preceding pages. - */ - private int insertedFootnotesLength = 0; - /** True if footnote citations have been met since the beginning of the page sequence. */ - private boolean footnotesPending = false; - /** - * True if the elements met after the previous break point contain footnote citations. - */ - private boolean newFootnotes = false; - /** - * Index of the first footnote met after the previous break point. - */ - private int firstNewFootnoteIndex = 0; - /** Index of the last footnote inserted on the current page. */ - private int footnoteListIndex = 0; - /** Index of the last element of the last footnote inserted on the current page. */ - private int footnoteElementIndex = -1; + private OutOfLineRecord footnotes; + private OutOfLineRecord floats; // 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 = null; + private int deferredFloatDemerits = 10000; // the method noBreakBetween(int, int) uses these variables // to store parameters and result of the last call, in order @@ -94,7 +65,7 @@ public PageBreakingAlgorithm(LayoutManager topLevelLM, PageSequenceLayoutManager.PageProvider pageProvider, int alignment, int alignmentLast, - MinOptMax footnoteSeparatorLength, + MinOptMax footnoteSeparatorLength, MinOptMax floatSeparatorLength, boolean partOverflowRecovery, boolean autoHeight, boolean favorSinglePart) { super(alignment, alignmentLast, true, partOverflowRecovery, 0); @@ -102,7 +73,8 @@ this.topLevelLM = topLevelLM; this.pageProvider = pageProvider; best = new BestPageRecords(); - this.footnoteSeparatorLength = (MinOptMax) footnoteSeparatorLength.clone(); + footnotes = new OutOfLineRecord((MinOptMax) footnoteSeparatorLength.clone()); + floats = new OutOfLineRecord((MinOptMax) floatSeparatorLength.clone()); // add some stretch, to avoid a restart for every page containing footnotes if (footnoteSeparatorLength.min == footnoteSeparatorLength.max) { footnoteSeparatorLength.max += 10000; @@ -115,29 +87,23 @@ * This class represents a feasible breaking point * with extra information about footnotes. */ - protected class KnuthPageNode extends KnuthNode { + public class KnuthPageNode extends KnuthNode { - /** Additional length due to footnotes. */ - public int totalFootnotes; + public OutOfLineRecord.ProgressInfo footnotesProgress; + public OutOfLineRecord.ProgressInfo floatsProgress; - /** 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, + OutOfLineRecord.ProgressInfo footnotesProgress, + OutOfLineRecord.ProgressInfo floatsProgress, 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.footnotesProgress = footnotesProgress.copy(); + this.floatsProgress = floatsProgress.copy(); } } @@ -148,48 +114,55 @@ */ protected class BestPageRecords extends BestRecords { - private int[] bestFootnotesLength = new int[4]; - private int[] bestFootnoteListIndex = new int[4]; - private int[] bestFootnoteElementIndex = new int[4]; - + private OutOfLineRecord.ProgressInfo[] bestFootnotesProgress + = new OutOfLineRecord.ProgressInfo[4]; + private OutOfLineRecord.ProgressInfo[] bestFloatsProgress + = new OutOfLineRecord.ProgressInfo[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; + bestFootnotesProgress[fitness] = footnotes.getProgress().copy(); + bestFloatsProgress[fitness] = floats.getProgress().copy(); } public int getFootnotesLength(int fitness) { - return bestFootnotesLength[fitness]; + return bestFootnotesProgress[fitness].getAlreadyInsertedLength(); } public int getFootnoteListIndex(int fitness) { - return bestFootnoteListIndex[fitness]; + return bestFootnotesProgress[fitness].getLastInsertedIndex(); } public int getFootnoteElementIndex(int fitness) { - return bestFootnoteElementIndex[fitness]; + return bestFootnotesProgress[fitness].getLastElementOfLastInsertedIndex(); } + + public OutOfLineRecord.ProgressInfo getFootnoteProgress(int fitness) { + return bestFootnotesProgress[fitness]; + } + + public OutOfLineRecord.ProgressInfo getFloatProgress(int fitness) { + return bestFloatsProgress[fitness]; + } } protected void initialize() { super.initialize(); - insertedFootnotesLength = 0; - footnoteListIndex = 0; - footnoteElementIndex = -1; + footnotes.initialize(); + floats.initialize(); } - protected KnuthNode createNode(int position, int line, int fitness, + public 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, + footnotes.getProgress(), floats.getProgress(), adjustRatio, availableShrink, availableStretch, difference, totalDemerits, previous); } @@ -198,9 +171,8 @@ 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), + ((BestPageRecords) best).getFootnoteProgress(fitness), + ((BestPageRecords) best).getFloatProgress(fitness), best.getAdjust(fitness), best.getAvailableShrink(fitness), best.getAvailableStretch(fitness), best.getDifference(fitness), best.getDemerits(fitness), best.getNode(fitness)); @@ -213,174 +185,171 @@ */ protected void handleBox(KnuthBox box) { if (box instanceof KnuthBlockBox - && ((KnuthBlockBox) box).hasAnchors()) { - handleFootnotes(((KnuthBlockBox) box).getElementLists()); - if (!newFootnotes) { - newFootnotes = true; - firstNewFootnoteIndex = footnotesList.size() - 1; - } + && ((KnuthBlockBox) box).hasFootnoteAnchors()) { + footnotes.add(((KnuthBlockBox) box).getFootnoteElementLists()); } - } - - /** - * Handles the footnotes cited inside a block-level box. Updates footnotesList and the - * value of totalFootnotesLength with the lengths of the given footnotes. - * @param elementLists list of KnuthElement sequences corresponding to the footnotes - * bodies - */ - private void handleFootnotes(LinkedList elementLists) { - // initialization - if (!footnotesPending) { - footnotesPending = true; - footnotesList = new ArrayList(); - lengthList = new ArrayList(); - totalFootnotesLength = 0; + if (box instanceof KnuthBlockBox + && ((KnuthBlockBox) box).hasFloatAnchors()) { + floats.add(((KnuthBlockBox) box).getFloatElementLists()); } - if (!newFootnotes) { - newFootnotes = true; - firstNewFootnoteIndex = footnotesList.size(); - } - - // compute the total length of the footnotes - ListIterator elementListsIterator = elementLists.listIterator(); - while (elementListsIterator.hasNext()) { - LinkedList noteList = (LinkedList) elementListsIterator.next(); - - //Space resolution (Note: this does not respect possible stacking constraints - //between footnotes!) - SpaceResolver.resolveElementList(noteList); - - 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 int restartFrom(KnuthNode restartingNode, int currentIndex) { int returnValue = super.restartFrom(restartingNode, currentIndex); - newFootnotes = false; - if (footnotesPending) { + footnotes.resetNewSinceLastBreakpoint(); + floats.resetNewSinceLastBreakpoint(); + if (footnotes.existing() || floats.existing()) { // 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()); + && ((KnuthBlockBox) resettedElement).hasFootnoteAnchors()) { + footnotes.reset(((KnuthBlockBox) resettedElement).getFootnoteElementLists()); } + if (resettedElement instanceof KnuthBlockBox + && ((KnuthBlockBox) resettedElement).hasFloatAnchors()) { + floats.reset(((KnuthBlockBox) resettedElement).getFloatElementLists());//TODO + } } } return returnValue; } - 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 footnotesPending; - if (footnotesList.size() == 0) { - footnotesPending = false; - } - } - protected void considerLegalBreak(KnuthElement element, int elementIdx) { super.considerLegalBreak(element, elementIdx); - newFootnotes = false; + footnotes.resetNewSinceLastBreakpoint(); + floats.resetNewSinceLastBreakpoint(); } protected int computeDifference(KnuthNode activeNode, KnuthElement element, int elementIndex) { KnuthPageNode pageNode = (KnuthPageNode) activeNode; int actualWidth = totalWidth - pageNode.totalWidth; - int footnoteSplit; - boolean canDeferOldFootnotes; if (element.isPenalty()) { actualWidth += element.getW(); } - if (footnotesPending) { + if (footnotes.existing()) { + footnotes.setProgress(pageNode.footnotesProgress); // compute the total length of the footnotes not yet inserted - int allFootnotes = totalFootnotesLength - pageNode.totalFootnotes; + int allFootnotes = footnotes.getTotalLength() + - pageNode.footnotesProgress.getAlreadyInsertedLength(); if (allFootnotes > 0) { // this page contains some footnote citations // add the footnote separator width - actualWidth += footnoteSeparatorLength.opt; + actualWidth += footnotes.getSeparatorLength().opt; if (actualWidth + allFootnotes <= getLineWidth()) { // there is enough space to insert all footnotes: // add the whole allFootnotes length actualWidth += allFootnotes; - insertedFootnotesLength = pageNode.totalFootnotes + allFootnotes; - footnoteListIndex = footnotesList.size() - 1; - footnoteElementIndex = ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1; - } else if (((canDeferOldFootnotes = checkCanDeferOldFootnotes(pageNode, elementIndex)) - || newFootnotes) - && (footnoteSplit = getFootnoteSplit(pageNode, getLineWidth() - actualWidth, - canDeferOldFootnotes)) > 0) { - // it is allowed to break or even defer footnotes if either: - // - there are new footnotes in the last piece of content, and - // there is space to add at least a piece of the first one - // - or the previous page break deferred some footnote lines, and - // this is the first feasible break; in this case it is allowed - // to break and defer, if necessary, old and new footnotes - actualWidth += footnoteSplit; - insertedFootnotesLength = pageNode.totalFootnotes + footnoteSplit; - // footnoteListIndex has been set in getFootnoteSplit() - // footnoteElementIndex has been set in getFootnoteSplit() + footnotes.insertAll(); } else { - // there is no space to add the smallest piece of footnote, - // or we are trying to add a piece of content with no footnotes and - // it does not fit in the page, because of previous footnote bodies - // that cannot be broken: - // add the whole allFootnotes length, so this breakpoint will be discarded - actualWidth += allFootnotes; - insertedFootnotesLength = pageNode.totalFootnotes + allFootnotes; - footnoteListIndex = footnotesList.size() - 1; - footnoteElementIndex = ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1; + boolean canDeferOldFootnotes = checkCanDeferOldOutOfLines(footnotes, + pageNode.position, elementIndex); + int footnoteSplit; + if ((canDeferOldFootnotes || footnotes.newSinceLastBreakpoint()) + && (footnoteSplit = footnotes.getFootnoteSplit( + pageNode.footnotesProgress, + getLineWidth() - actualWidth, canDeferOldFootnotes)) > 0) { + // it is allowed to break or even defer footnotes if either: + // - there are new footnotes in the last piece of content, and + // there is space to add at least a piece of the first one + // - or the previous page break deferred some footnote lines, and + // this is the first feasible break; in this case it is allowed + // to break and defer, if necessary, old and new footnotes + actualWidth += footnoteSplit; + } else { + // there is no space to add the smallest piece of footnote, + // or we are trying to add a piece of content with no footnotes and + // it does not fit in the page, because of previous footnote bodies + // that cannot be broken: + // add the whole allFootnotes length, so this breakpoint will be discarded + actualWidth += allFootnotes; + footnotes.insertAll(); + } } - } else { - // all footnotes have already been placed on previous pages + } // else: all footnotes have already been placed on previous pages + } + if (floats.existing()) { + floats.setProgress(pageNode.floatsProgress); + // compute the total length of the floats not yet inserted + int allFloats = floats.getTotalLength() + - pageNode.floatsProgress.getAlreadyInsertedLength(); + if (allFloats > 0 + && getLineWidth() - actualWidth - floats.getSeparatorLength().opt > 0) { + // this page contains some float citations + // add the float separator width + int split = floats.getFloatSplit(pageNode.floatsProgress, + getLineWidth() - actualWidth - floats.getSeparatorLength().opt); + if (split > 0) { + actualWidth += floats.getSeparatorLength().opt + split; + } } - } else { - // there are no footnotes } + /* Another algorithm exactly mimicing the handling of footnotes: it should force + * more floats to be on the same page as their citations, at the price of more + * underfull pages (thus a higher total number of pages). If the current method + * works well enough, we may keep it. + */ +// if (floats.existing()) { +// floats.setProgress(pageNode.floatsProgress); +// // compute the total length of the floats not yet inserted +// int allFloats = floats.getTotalLength() +// - pageNode.floatsProgress.getAlreadyInsertedLength(); +// if (allFloats > 0) { +// // this page contains some float citations +// // add the float separator width +// actualWidth += floats.getSeparatorLength().opt; +// if (actualWidth + allFloats <= getLineWidth()) { +// // there is enough space to insert all floats: +// // add the whole allFloats length +// actualWidth += allFloats; +// floats.insertAll(); +// } else { +// boolean canDeferOldFloats = checkCanDeferOldOutOfLines(floats, +// pageNode.position, elementIndex); +// int floatSplit; +// if ((canDeferOldFloats || floats.newSinceLastBreakpoint()) +// && (floatSplit = floats.getFloatSplit( +// pageNode.floatsProgress, +// getLineWidth() - actualWidth)) > 0) { +// actualWidth += floatSplit; +// } else { +// actualWidth += allFloats; +// floats.insertAll(); +// } +// } +// } // else: all floats have already been placed on previous pages +// } return getLineWidth(activeNode.line) - actualWidth; } - /** Checks whether footnotes from preceding pages may be deferred to the page after - * the given element. - * @param node active node for the preceding page break - * @param contentElementIndex index of the Knuth element considered for the - * current page break + /** + * Checks whether out-of-line objects from preceding pages may be deferred + * to the page after the given element. + * + * @param outOfLine informations about the out-of-line objects + * @param activeNodePosition index in the Knuth sequence of the currently considered + * active node + * @param contentElementIndex index in the Knuth sequence of the currently considered + * legal breakpoint + * @return true if it is allowed to defer some out-of-line objects on + * following pages */ - private boolean checkCanDeferOldFootnotes(KnuthPageNode node, int contentElementIndex) { - return (noBreakBetween(node.position, contentElementIndex) - && deferredFootnotes(node.footnoteListIndex, node.footnoteElementIndex, node.totalFootnotes)); + private boolean checkCanDeferOldOutOfLines(OutOfLineRecord outOfLine, + int activeNodePosition, + int contentElementIndex) { + return (noBreakBetween(activeNodePosition, contentElementIndex) + && outOfLine.deferred()); } /** - * Returns true if there may be no breakpoint between the two given elements. + * Returns true if there is no legal breakpoint between the two given elements. * @param prevBreakIndex index of the element from the currently considered active * node * @param breakIndex index of the currently considered breakpoint - * @return true if no element between the two can be a breakpoint + * @return true if no element between the two is a legal breakpoint */ private boolean noBreakBetween(int prevBreakIndex, int breakIndex) { // this method stores the parameters and the return value from previous calls @@ -425,164 +394,18 @@ return storedValue; } - /** - * Returns true if their are (pieces of) footnotes to be typeset on the current page. - * @param listIndex index of the last inserted footnote for the currently considered - * active node - * @param elementIndex index of the last element of the last inserted footnote - * @param length total length of all footnotes inserted so far - */ - private boolean deferredFootnotes(int listIndex, int elementIndex, int length) { - return ((newFootnotes - && firstNewFootnoteIndex != 0 - && (listIndex < firstNewFootnoteIndex - 1 - || elementIndex < ((LinkedList) footnotesList.get(listIndex)).size() - 1)) - || length < totalFootnotesLength); - } - - /** - * Tries to split the flow of footnotes to put one part on the current page. - * @param activeNode currently considered previous page break - * @param availableLength available space for footnotes - * @param canDeferOldFootnotes - */ - private int getFootnoteSplit(KnuthPageNode activeNode, int availableLength, boolean canDeferOldFootnotes) { - return getFootnoteSplit(activeNode.footnoteListIndex, - activeNode.footnoteElementIndex, - activeNode.totalFootnotes, - availableLength, canDeferOldFootnotes); - } - - /** - * Tries to split the flow of footnotes to put one part on the current page. - * @param prevListIndex index of the last footnote on the previous page - * @param prevElementIndex index of the last element of the last footnote - * @param prevLength total length of footnotes inserted so far - * @param availableLength available space for footnotes on this page - * @param canDeferOldFootnotes - */ - private int getFootnoteSplit(int prevListIndex, int prevElementIndex, int prevLength, - int availableLength, boolean canDeferOldFootnotes) { - if (availableLength <= 0) { - return 0; - } else { - // the split should contain a piece of the last footnote - // together with all previous, not yet inserted footnotes; - // but if this is not possible, try adding as much content as possible - int splitLength = 0; - ListIterator noteListIterator = null; - KnuthElement element = null; - boolean somethingAdded = false; - - // prevListIndex and prevElementIndex points to the last footnote element - // already placed in a page: advance to the next element - int listIndex = prevListIndex; - int elementIndex = prevElementIndex; - if (elementIndex == ((LinkedList) footnotesList.get(listIndex)).size() - 1) { - listIndex++; - elementIndex = 0; - } else { - elementIndex++; - } - - // try adding whole notes - if (footnotesList.size() - 1 > listIndex) { - // add the previous footnotes: these cannot be broken or deferred - if (!canDeferOldFootnotes - && newFootnotes - && firstNewFootnoteIndex > 0) { - splitLength = ((Integer) lengthList.get(firstNewFootnoteIndex - 1)).intValue() - - prevLength; - listIndex = firstNewFootnoteIndex; - elementIndex = 0; - } - // try adding the new footnotes - while (((Integer) lengthList.get(listIndex)).intValue() - prevLength - <= availableLength) { - splitLength = ((Integer) lengthList.get(listIndex)).intValue() - - prevLength; - somethingAdded = true; - listIndex++; - elementIndex = 0; - } - // as this method is called only if it is not possible to insert - // all footnotes, at this point listIndex and elementIndex points to - // an existing element, the next one we will try to insert - } - - // try adding a split of the next note - noteListIterator = ((LinkedList) footnotesList.get(listIndex)).listIterator(elementIndex); - - int prevSplitLength = 0; - int prevIndex = -1; - int index = -1; - - while (!(somethingAdded && splitLength > availableLength)) { - if (!somethingAdded) { - somethingAdded = true; - } else { - prevSplitLength = splitLength; - prevIndex = index; - } - // get a sub-sequence from the note element list - boolean bPrevIsBox = false; - while (noteListIterator.hasNext()) { - // as this method is called only if it is not possible to insert - // all footnotes, and we have already tried (and failed) to insert - // this whole footnote, the while loop will never reach the end - // of the note sequence - 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) { - // end of the sub-sequence - 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 (!somethingAdded) { - // there was not enough space to add a piece of the first new footnote - // this is not a good break - prevSplitLength = 0; - } else if (prevSplitLength > 0) { - // prevIndex is -1 if we have added only some whole footnotes - footnoteListIndex = (prevIndex != -1) ? listIndex : listIndex - 1; - footnoteElementIndex = (prevIndex != -1) - ? prevIndex - : ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1; - } - return prevSplitLength; - } - } - 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 (((KnuthPageNode) activeNode).footnotesProgress.getAlreadyInsertedLength() < footnotes.getTotalLength()) { + maxAdjustment += footnotes.getSeparatorLength().max - footnotes.getSeparatorLength().opt; } + // add the float separator stretch if some float content will be added + if (((KnuthPageNode) activeNode).floatsProgress.getAlreadyInsertedLength() < floats.getTotalLength()) { + maxAdjustment += floats.getSeparatorLength().max - floats.getSeparatorLength().opt; + } if (maxAdjustment > 0) { return (double) difference / maxAdjustment; } else { @@ -591,9 +414,13 @@ } 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 (((KnuthPageNode) activeNode).footnotesProgress.getAlreadyInsertedLength() < footnotes.getTotalLength()) { + maxAdjustment += footnotes.getSeparatorLength().opt - footnotes.getSeparatorLength().min; } + // add the float separator shrink if some float content will be added + if (((KnuthPageNode) activeNode).floatsProgress.getAlreadyInsertedLength() < floats.getTotalLength()) { + maxAdjustment += floats.getSeparatorLength().opt - floats.getSeparatorLength().min; + } if (maxAdjustment > 0) { return (double) difference / maxAdjustment; } else { @@ -609,6 +436,19 @@ double demerits = 0; // compute demerits double f = Math.abs(r); + /* If the adjustment ratio is too high, the demerits will be "almost infinite" + * (10^22). Adding demerits for a deferred float (10000) thus won't change the + * demerits value. We may end up with two breakpoints with the same demerits, + * whereas in one case there are deferred floats and not in the other case. The + * case with no deferred floats is still preferable, so we must have the + * possibility to distinguish it. By forcing f to 1 it becomes possible to make + * the difference when there are deferred floats. + * TODO vh: use threshold instead of 1 (currently threshold == 1 but it might be + * configurable) + */ + if (f > 1) { + f = 1; + } f = 1 + 100 * f * f * f; if (element.isPenalty() && element.getP() >= 0) { f += element.getP(); @@ -632,18 +472,15 @@ demerits += incompatibleFitnessDemerit; } - if (footnotesPending) { - if (footnoteListIndex < footnotesList.size() - 1) { - // add demerits for the deferred footnotes - demerits += (footnotesList.size() - 1 - footnoteListIndex) - * deferredFootnoteDemerits; - } - if (footnoteElementIndex - < ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1) { - // add demerits for the footnote split between pages + if (footnotes.existing()) { + demerits += footnotes.getNbOfDeferred() * deferredFootnoteDemerits; + if (footnotes.isSplit()) { demerits += splitFootnoteDemerits; } } + if (floats.existing()) { + demerits += floats.getNbOfDeferred() * deferredFloatDemerits; + } demerits += activeNode.totalDemerits; return demerits; } @@ -653,66 +490,19 @@ for (KnuthPageNode node = (KnuthPageNode) getNode(i); node != null; node = (KnuthPageNode) node.next) { - if (node.totalFootnotes < totalFootnotesLength) { + if (node.footnotesProgress.getAlreadyInsertedLength() + < footnotes.getTotalLength()) { // layout remaining footnote bodies - createFootnotePages(node); + footnotes.createFootnotePages(node, this, getLineWidth()); } + if (node.floatsProgress.getAlreadyInsertedLength() < floats.getTotalLength()) { + // layout remaining float bodies + floats.createFloatPages(node, this, getLineWidth()); + } } } } - private void createFootnotePages(KnuthPageNode lastNode) { - insertedFootnotesLength = lastNode.totalFootnotes; - footnoteListIndex = lastNode.footnoteListIndex; - footnoteElementIndex = lastNode.footnoteElementIndex; - int availableBPD = getLineWidth(); - int split = 0; - KnuthPageNode prevNode = lastNode; - - // create pages containing the remaining footnote bodies - while (insertedFootnotesLength < totalFootnotesLength) { - // try adding some more content - if (((Integer) lengthList.get(footnoteListIndex)).intValue() - insertedFootnotesLength - <= availableBPD) { - // add a whole footnote - availableBPD -= ((Integer) lengthList.get(footnoteListIndex)).intValue() - - insertedFootnotesLength; - insertedFootnotesLength = ((Integer)lengthList.get(footnoteListIndex)).intValue(); - footnoteElementIndex - = ((LinkedList)footnotesList.get(footnoteListIndex)).size() - 1; - } else if ((split = getFootnoteSplit(footnoteListIndex, footnoteElementIndex, - insertedFootnotesLength, availableBPD, true)) - > 0) { - // add a piece of a footnote - availableBPD -= split; - insertedFootnotesLength += split; - // footnoteListIndex has already been set in getFootnoteSplit() - // footnoteElementIndex has already been set in getFootnoteSplit() - } else { - // cannot add any content: create a new node and start again - KnuthPageNode node = (KnuthPageNode) - createNode(lastNode.position, prevNode.line + 1, 1, - insertedFootnotesLength - prevNode.totalFootnotes, - 0, 0, - 0, 0, 0, - 0, 0, prevNode); - addNode(node.line, node); - removeNode(prevNode.line, prevNode); - - prevNode = node; - availableBPD = getLineWidth(); - } - } - // create the last node - KnuthPageNode node = (KnuthPageNode) - createNode(lastNode.position, prevNode.line + 1, 1, - totalFootnotesLength - prevNode.totalFootnotes, 0, 0, - 0, 0, 0, - 0, 0, prevNode); - addNode(node.line, node); - removeNode(prevNode.line, prevNode); - } - /** * @return a list of PageBreakPosition elements */ @@ -781,16 +571,25 @@ } } // 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) { + int firstFootnoteListIndex = ((KnuthPageNode) bestActiveNode.previous). + footnotesProgress.getLastInsertedIndex(); + int firstFootnoteElementIndex = ((KnuthPageNode) bestActiveNode.previous). + footnotesProgress.getLastElementOfLastInsertedIndex(); + if (firstFootnoteListIndex == -1) { + firstFootnoteListIndex++; + firstFootnoteElementIndex = 0; + } else if (footnotes.getSequence(firstFootnoteListIndex) != null + && firstFootnoteElementIndex == ((LinkedList) footnotes. + getSequence(firstFootnoteListIndex)).size() - 1) { // advance to the next list - firstListIndex++; - firstElementIndex = 0; + firstFootnoteListIndex++; + firstFootnoteElementIndex = 0; } else { - firstElementIndex++; + firstFootnoteElementIndex++; } + // compute the indexes of the first float list + int firstFloatListIndex = ((KnuthPageNode) bestActiveNode.previous). + floatsProgress.getLastInsertedIndex() + 1; // add nodes at the beginning of the list, as they are found // backwards, from the last one to the first one @@ -800,9 +599,12 @@ } insertPageBreakAsFirst(new PageBreakPosition(this.topLevelLM, bestActiveNode.position, - firstListIndex, firstElementIndex, - ((KnuthPageNode) bestActiveNode).footnoteListIndex, - ((KnuthPageNode) bestActiveNode).footnoteElementIndex, + firstFootnoteListIndex, firstFootnoteElementIndex, + ((KnuthPageNode) bestActiveNode).footnotesProgress.getLastInsertedIndex(), + ((KnuthPageNode) bestActiveNode).footnotesProgress. + getLastElementOfLastInsertedIndex(), + firstFloatListIndex, + ((KnuthPageNode) bestActiveNode).floatsProgress.getLastInsertedIndex(), ratio, difference)); } @@ -829,9 +631,13 @@ } public LinkedList getFootnoteList(int index) { - return (LinkedList) footnotesList.get(index); + return (LinkedList) footnotes.getSequence(index); } - + + public LinkedList getFloatList(int index) { + return (LinkedList) floats.getSequence(index); + } + /** @return the associated top-level formatting object. */ public FObj getFObj() { return topLevelLM.getFObj(); Index: src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java (révision 423415) +++ src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java (copie de travail) @@ -26,6 +26,7 @@ import org.apache.fop.area.AreaTreeHandler; import org.apache.fop.area.AreaTreeModel; import org.apache.fop.area.Block; +import org.apache.fop.area.BeforeFloat; import org.apache.fop.area.Footnote; import org.apache.fop.area.PageViewport; import org.apache.fop.area.LineArea; @@ -88,7 +89,8 @@ private int startPageNum = 0; private int currentPageNum = 0; - private Block separatorArea = null; + private Block footnoteSeparatorArea = null; + private Block floatSeparatorArea = null; /** * Constructor @@ -179,6 +181,7 @@ private boolean needColumnBalancing; private StaticContentLayoutManager footnoteSeparatorLM = null; + private StaticContentLayoutManager floatSeparatorLM = null; public PageBreaker(PageSequenceLayoutManager pslm) { this.pslm = pslm; @@ -241,38 +244,61 @@ } // scan contentList, searching for footnotes - boolean bFootnotesPresent = false; + boolean footnotesPresent = false; + boolean floatsPresent = false; if (contentList != null) { ListIterator contentListIterator = contentList.listIterator(); while (contentListIterator.hasNext()) { ListElement element = (ListElement) contentListIterator.next(); - if (element instanceof KnuthBlockBox - && ((KnuthBlockBox) element).hasAnchors()) { - // element represents a line with footnote citations - bFootnotesPresent = true; - LayoutContext footnoteContext = new LayoutContext(context); - footnoteContext.setStackLimit(context.getStackLimit()); - footnoteContext.setRefIPD(getCurrentPV() - .getRegionReference(Constants.FO_REGION_BODY).getIPD()); - 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); - fblm.initialize(); - ((KnuthBlockBox) element).addElementList( - fblm.getNextKnuthElements(footnoteContext, alignment)); + if (element instanceof KnuthBlockBox) { + if (((KnuthBlockBox) element).hasFootnoteAnchors()) { + // element represents a line with footnote citations + footnotesPresent = true; + LayoutContext footnoteContext = new LayoutContext(context); + footnoteContext.setStackLimit(context.getStackLimit()); + footnoteContext.setRefIPD(getCurrentPV() + .getRegionReference(Constants.FO_REGION_BODY).getIPD()); + 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); + fblm.initialize(); + ((KnuthBlockBox) element).addFootnoteElementList( + fblm.getNextKnuthElements(footnoteContext, alignment)); + } } + if (((KnuthBlockBox) element).hasFloatAnchors()) { + // element represents a line with float citations + floatsPresent = true; + LayoutContext floatContext = new LayoutContext(context); + floatContext.setStackLimit(context.getStackLimit()); + floatContext.setRefIPD(getCurrentPV() + .getRegionReference(Constants.FO_REGION_BODY).getIPD()); + LinkedList floatBodyLMs = ((KnuthBlockBox) element).getFloatBodyLMs(); + ListIterator floatBodyIterator = floatBodyLMs.listIterator(); + // store the lists of elements representing the footnote bodies + // in the box representing the line containing their references + while (floatBodyIterator.hasNext()) { + FloatBodyLayoutManager fblm + = (FloatBodyLayoutManager) floatBodyIterator.next(); + fblm.setParent(childFLM); + fblm.initialize(); + ((KnuthBlockBox) element).addFloatElementList( + fblm.getNextKnuthElements(floatContext, alignment)); + } + } } } } // handle the footnote separator StaticContent footnoteSeparator; - if (bFootnotesPresent + if (footnotesPresent && (footnoteSeparator = pageSeq.getStaticContent( "xsl-footnote-separator")) != null) { // the footnote separator can contain page-dependent content such as @@ -283,17 +309,42 @@ // always the same // create a Block area that will contain the separator areas - separatorArea = new Block(); - separatorArea.setIPD(pslm.getCurrentPV() + footnoteSeparatorArea = new Block(); + footnoteSeparatorArea.setIPD(pslm.getCurrentPV() .getRegionReference(Constants.FO_REGION_BODY).getIPD()); // create a StaticContentLM for the footnote separator footnoteSeparatorLM = (StaticContentLayoutManager) getLayoutManagerMaker().makeStaticContentLayoutManager( - pslm, footnoteSeparator, separatorArea); + pslm, footnoteSeparator, footnoteSeparatorArea); footnoteSeparatorLM.doLayout(); - footnoteSeparatorLength = new MinOptMax(separatorArea.getBPD()); + footnoteSeparatorLength = new MinOptMax(footnoteSeparatorArea.getBPD()); } + + // handle the float separator + StaticContent floatSeparator; + if (floatsPresent + && (floatSeparator = pageSeq.getStaticContent( + "xsl-before-float-separator")) != null) { + // the float separator can contain page-dependent content such as + // page numbers or retrieve markers, so its areas cannot simply be + // obtained now and repeated in each page; + // we need to know in advance the separator bpd: the actual separator + // could be different from page to page, but its bpd would likely be + // always the same + + // create a Block area that will contain the separator areas + floatSeparatorArea = new Block(); + floatSeparatorArea.setIPD(pslm.getCurrentPV() + .getRegionReference(Constants.FO_REGION_BODY).getIPD()); + // create a StaticContentLM for the float separator + floatSeparatorLM = (StaticContentLayoutManager) + getLayoutManagerMaker().makeStaticContentLayoutManager( + pslm, floatSeparator, floatSeparatorArea); + floatSeparatorLM.doLayout(); + + floatSeparatorLength = new MinOptMax(floatSeparatorArea.getBPD()); + } return contentList; } @@ -311,15 +362,28 @@ StaticContent footnoteSeparator = pageSeq.getStaticContent( "xsl-footnote-separator"); // create a Block area that will contain the separator areas - separatorArea = new Block(); - separatorArea.setIPD( + footnoteSeparatorArea = new Block(); + footnoteSeparatorArea.setIPD( getCurrentPV().getRegionReference(Constants.FO_REGION_BODY).getIPD()); // create a StaticContentLM for the footnote separator footnoteSeparatorLM = (StaticContentLayoutManager) getLayoutManagerMaker().makeStaticContentLayoutManager( - pslm, footnoteSeparator, separatorArea); + pslm, footnoteSeparator, footnoteSeparatorArea); footnoteSeparatorLM.doLayout(); } + if (floatSeparatorLM != null) { + StaticContent floatSeparator = pageSeq.getStaticContent( + "xsl-before-float-separator"); + // create a Block area that will contain the separator areas + floatSeparatorArea = new Block(); + floatSeparatorArea.setIPD( + getCurrentPV().getRegionReference(Constants.FO_REGION_BODY).getIPD()); + // create a StaticContentLM for the float separator + floatSeparatorLM = (StaticContentLayoutManager) + getLayoutManagerMaker().makeStaticContentLayoutManager( + pslm, floatSeparator, floatSeparatorArea); + floatSeparatorLM.doLayout(); + } childFLM.addAreas(posIter, context); } @@ -373,7 +437,7 @@ getTopLevelLM(), getPageProvider(), alg.getAlignment(), alg.getAlignmentLast(), - footnoteSeparatorLength, + footnoteSeparatorLength, floatSeparatorLength, isPartOverflowRecoveryActivated(), false, false); //alg.setConstantLineWidth(flowBPD); int iOptPageCount = algRestart.findBreakingPoints(effectiveList, @@ -432,7 +496,7 @@ PageBreakingAlgorithm algRestart = new BalancingColumnBreakingAlgorithm( getTopLevelLM(), getPageProvider(), - alignment, Constants.EN_START, footnoteSeparatorLength, + alignment, Constants.EN_START, footnoteSeparatorLength, floatSeparatorLength, isPartOverflowRecoveryActivated(), getCurrentPV().getBodyRegion().getColumnCount()); //alg.setConstantLineWidth(flowBPD); @@ -487,6 +551,26 @@ } protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) { + // add float areas + if (pbp.floatFirstListIndex <= pbp.floatLastListIndex) { + // call addAreas() for each FloatBodyLM + for (int i = pbp.floatFirstListIndex; i <= pbp.floatLastListIndex; i++) { + LinkedList elementList = alg.getFloatList(i); + int firstIndex = 0; + int lastIndex = elementList.size() - 1; + + SpaceResolver.performConditionalsNotification(elementList, + firstIndex, lastIndex, -1); + LayoutContext childLC = new LayoutContext(0); + AreaAdditionUtil.addAreas(null, + new KnuthPossPosIter(elementList, firstIndex, lastIndex + 1), + childLC); + } + // set the offset from the top margin + BeforeFloat parentArea + = (BeforeFloat) getCurrentPV().getBodyRegion().getBeforeFloat(); + parentArea.setSeparator(floatSeparatorArea); + } // add footnote areas if (pbp.footnoteFirstListIndex < pbp.footnoteLastListIndex || pbp.footnoteFirstElementIndex <= pbp.footnoteLastElementIndex) { @@ -507,12 +591,13 @@ } // set the offset from the top margin Footnote parentArea = (Footnote) getCurrentPV().getBodyRegion().getFootnote(); - int topOffset = (int) getCurrentPV().getBodyRegion().getBPD() - parentArea.getBPD(); - if (separatorArea != null) { - topOffset -= separatorArea.getBPD(); + int topOffset = (int) getCurrentPV().getBodyRegion().getBPD() - parentArea.getBPD() + - ((BeforeFloat) getCurrentPV().getBodyRegion().getBeforeFloat()).getBPD(); + if (footnoteSeparatorArea != null) { + topOffset -= footnoteSeparatorArea.getBPD(); } parentArea.setTop(topOffset); - parentArea.setSeparator(separatorArea); + parentArea.setSeparator(footnoteSeparatorArea); } getCurrentPV().getCurrentSpan().notifyFlowsFinished(); } Index: src/java/org/apache/fop/layoutmgr/inline/KnuthInlineBox.java =================================================================== --- src/java/org/apache/fop/layoutmgr/inline/KnuthInlineBox.java (révision 423415) +++ src/java/org/apache/fop/layoutmgr/inline/KnuthInlineBox.java (copie de travail) @@ -19,6 +19,7 @@ package org.apache.fop.layoutmgr.inline; import org.apache.fop.layoutmgr.inline.AlignmentContext; +import org.apache.fop.layoutmgr.FloatBodyLayoutManager; import org.apache.fop.layoutmgr.FootnoteBodyLayoutManager; import org.apache.fop.layoutmgr.KnuthBox; import org.apache.fop.layoutmgr.Position; @@ -26,6 +27,7 @@ public class KnuthInlineBox extends KnuthBox { private FootnoteBodyLayoutManager footnoteBodyLM = null; + private FloatBodyLayoutManager floatBodyLM = null; private AlignmentContext alignmentContext = null; /** @@ -63,16 +65,43 @@ } /** - * @return true if this box holds a reference to a FootnoteBodyLM + * @param fblm the FloatBodyLM this box must hold a reference to */ + public void setFloatBodyLM(FloatBodyLayoutManager fblm) { + floatBodyLM = fblm; + } + + /** + * @return the FloatBodyLM this box holds a reference to + */ + public FloatBodyLayoutManager getFloatBodyLM() { + return floatBodyLM; + } + + /** + * @return true if this box holds a reference to an out-of-line object + */ public boolean isAnchor() { + return (footnoteBodyLM != null || floatBodyLM != null); + } + + /** + * @return true if this box holds a reference to a footnote + */ + public boolean isFootnoteAnchor() { return (footnoteBodyLM != null); } - - + + /** + * @return true if this box holds a reference to a before-float + */ + public boolean isFloatAnchor() { + return (floatBodyLM != null); + } + /** @see java.lang.Object#toString() */ public String toString() { StringBuffer sb = new StringBuffer(super.toString()); return sb.toString(); } -} \ No newline at end of file +} Index: src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java (révision 423415) +++ src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java (copie de travail) @@ -1093,14 +1093,20 @@ // create a list of the FootnoteBodyLM handling footnotes // whose citations are in this line LinkedList footnoteList = new LinkedList(); + LinkedList floatList = new LinkedList(); ListIterator elementIterator = seq.listIterator(startIndex); while (elementIterator.nextIndex() <= endIndex) { KnuthElement element = (KnuthElement) elementIterator.next(); if (element instanceof KnuthInlineBox && ((KnuthInlineBox) element).isAnchor()) { - footnoteList.add(((KnuthInlineBox) element).getFootnoteBodyLM()); + if (((KnuthInlineBox) element).isFootnoteAnchor()) { + footnoteList.add(((KnuthInlineBox) element).getFootnoteBodyLM()); + } else { + floatList.add(((KnuthInlineBox) element).getFloatBodyLM()); + } } else if (element instanceof KnuthBlockBox) { footnoteList.addAll(((KnuthBlockBox) element).getFootnoteBodyLMs()); + floatList.addAll(((KnuthBlockBox) element).getFloatBodyLMs()); } } startIndex = endIndex + 1; @@ -1108,7 +1114,7 @@ = (LineBreakPosition) llPoss.getChosenPosition(i); returnList.add(new KnuthBlockBox (lbp.lineHeight + lbp.spaceBefore + lbp.spaceAfter, - footnoteList, lbp, false)); + footnoteList, floatList, lbp, false)); /* // add stretch and shrink to the returnlist if (!seq.isInlineSequence() && lbp.availableStretch != 0 || lbp.availableShrink != 0) { Index: src/java/org/apache/fop/layoutmgr/inline/FloatLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/inline/FloatLayoutManager.java (révision 0) +++ src/java/org/apache/fop/layoutmgr/inline/FloatLayoutManager.java (révision 0) @@ -0,0 +1,125 @@ +/* + * Copyright 1999-2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.layoutmgr.inline; + +import java.util.LinkedList; +import java.util.List; + +import org.apache.fop.fo.flow.Float; +import org.apache.fop.layoutmgr.AbstractLayoutManager; +import org.apache.fop.layoutmgr.FloatBodyLayoutManager; +import org.apache.fop.layoutmgr.InlineKnuthSequence; +import org.apache.fop.layoutmgr.KnuthElement; +import org.apache.fop.layoutmgr.KnuthSequence; +import org.apache.fop.layoutmgr.LayoutContext; +import org.apache.fop.layoutmgr.Position; + +/** + * Layout manager for fo:float. + */ +public class FloatLayoutManager extends AbstractLayoutManager + implements InlineLevelLayoutManager { + + private Float floatNode; + private FloatBodyLayoutManager bodyLM; + /** Represents the float citation **/ + private KnuthElement forcedAnchor; + + /** + * Create a new float layout manager. + * @param node float to create the layout manager for + */ + public FloatLayoutManager(Float node) { + super(node); + floatNode = node; + } + + /** @see org.apache.fop.layoutmgr.LayoutManager#initialize() */ + public void initialize() { + // create a FloatBodyLM handling the fo:float-body child of fo:float + bodyLM = new FloatBodyLayoutManager(floatNode); + } + + /** @see org.apache.fop.layoutmgr.LayoutManager */ + public LinkedList getNextKnuthElements(LayoutContext context, + int alignment) { + // this is the only method that must be implemented: + // all other methods will never be called, as the returned elements + // contain Positions created by the citationLM, so its methods will + // be called instead + + bodyLM.setParent(this); + bodyLM.initialize(); + + // get Knuth elements representing the float citation + LinkedList returnedList = new LinkedList(); + //Inline part of the float is empty. Need to send back an auxiliary + //zero-width, zero-height inline box so the float gets painted. + KnuthSequence seq = new InlineKnuthSequence(); + //Need to use an aux. box, otherwise, the line height can't be forced to zero height. + forcedAnchor = new KnuthInlineBox(0, null, null, true); + ((KnuthInlineBox) forcedAnchor).setFloatBodyLM(bodyLM); + seq.add(forcedAnchor); + returnedList.add(seq); + setFinished(true); + + return returnedList; + } + + /** @see org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager */ + public List addALetterSpaceTo(List oldList) { + log.warn("null implementation of addALetterSpaceTo() called!"); + return oldList; + } + + /** + * Remove the word space represented by the given elements + * + * @param oldList the elements representing the word space + */ + public void removeWordSpace(List oldList) { + // do nothing + log.warn(this.getClass().getName() + " should not receive a call to removeWordSpace(list)"); + } + + /** @see org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager */ + public void getWordChars(StringBuffer sbChars, Position pos) { + log.warn("null implementation of getWordChars() called!"); + } + + /** @see org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager */ + public void hyphenate(Position pos, HyphContext hc) { + log.warn("null implementation of hyphenate called!"); + } + + /** @see org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager */ + public boolean applyChanges(List oldList) { + log.warn("null implementation of applyChanges() called!"); + return false; + } + + /** + * @see org.apache.fop.layoutmgr.LayoutManager#getChangedKnuthElements(java.util.List, int) + */ + public LinkedList getChangedKnuthElements(List oldList, + int alignment) { + log.warn("null implementation of getChangeKnuthElement() called!"); + return null; + } +} Index: src/java/org/apache/fop/layoutmgr/AbstractBreaker.java =================================================================== --- src/java/org/apache/fop/layoutmgr/AbstractBreaker.java (révision 423415) +++ src/java/org/apache/fop/layoutmgr/AbstractBreaker.java (copie de travail) @@ -42,17 +42,22 @@ int footnoteFirstElementIndex; int footnoteLastListIndex; int footnoteLastElementIndex; + int floatFirstListIndex; + int floatLastListIndex; PageBreakPosition(LayoutManager lm, int iBreakIndex, - int ffli, int ffei, int flli, int flei, + int fofli, int fofei, int folli, int folei, + int flfli, int fllli, double bpdA, int diff) { super(lm, iBreakIndex); bpdAdjust = bpdA; difference = diff; - footnoteFirstListIndex = ffli; - footnoteFirstElementIndex = ffei; - footnoteLastListIndex = flli; - footnoteLastElementIndex = flei; + footnoteFirstListIndex = fofli; + footnoteFirstElementIndex = fofei; + footnoteLastListIndex = folli; + footnoteLastElementIndex = folei; + floatFirstListIndex = flfli; + floatLastListIndex = fllli; } } @@ -162,6 +167,7 @@ private int alignmentLast; protected MinOptMax footnoteSeparatorLength = new MinOptMax(0); + protected MinOptMax floatSeparatorLength = new MinOptMax(0); protected abstract int getCurrentDisplayAlign(); protected abstract boolean hasMoreContent(); @@ -310,7 +316,7 @@ + "), flow BPD =" + flowBPD); PageBreakingAlgorithm alg = new PageBreakingAlgorithm(getTopLevelLM(), getPageProvider(), - alignment, alignmentLast, footnoteSeparatorLength, + alignment, alignmentLast, footnoteSeparatorLength, floatSeparatorLength, isPartOverflowRecoveryActivated(), autoHeight, isSinglePartFavored()); int iOptPageCount; Index: src/java/org/apache/fop/layoutmgr/FloatBodyLayoutManager.java =================================================================== --- src/java/org/apache/fop/layoutmgr/FloatBodyLayoutManager.java (révision 0) +++ src/java/org/apache/fop/layoutmgr/FloatBodyLayoutManager.java (révision 0) @@ -0,0 +1,92 @@ +/* + * Copyright 1999-2006 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.layoutmgr; + +import java.util.LinkedList; + +import org.apache.fop.area.Area; +import org.apache.fop.fo.flow.Float; + +/** + * LayoutManager for the out-of-line area generated by fo:float nodes. + * @see org.apache.fop.layoutmgr.inline.FloatLayoutManager + */ +public class FloatBodyLayoutManager extends BlockStackingLayoutManager { + + public FloatBodyLayoutManager(Float node) { + super(node); + } + + /** @see org.apache.fop.layoutmgr.LayoutManager#addAreas(PositionIterator, LayoutContext) + * @deprecated */ + public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) { + LayoutManager childLM = null; + LayoutManager lastLM = null; + LayoutContext lc = new LayoutContext(0); + + // "unwrap" the NonLeafPositions stored in parentIter + // and put them in a new list; + LinkedList positionList = new LinkedList(); + Position pos; + while (parentIter.hasNext()) { + pos = (Position) parentIter.next(); + //log.trace("pos = " + pos.getClass().getName() + "; " + pos); + Position innerPosition = pos; + if (pos instanceof NonLeafPosition) { + innerPosition = ((NonLeafPosition) pos).getPosition(); + if (innerPosition.getLM() == this) { + // pos was created by this LM and was inside a penalty + // allowing or forbidding a page break + // nothing to do + //log.trace(" penalty"); + } else { + // innerPosition was created by another LM + positionList.add(innerPosition); + lastLM = innerPosition.getLM(); + //log.trace(" " + innerPosition.getClass().getName()); + } + } + } + + // the Positions in positionList were inside the elements + // created by the LineLM + StackingIter childPosIter = new StackingIter(positionList.listIterator()); + + while ((childLM = childPosIter.getNextChildLM()) != null) { + // set last area flag + lc.setFlags(LayoutContext.LAST_AREA, + (layoutContext.isLastArea() && childLM == lastLM)); + // Add the line areas to Area + childLM.addAreas(childPosIter, lc); + } + } + + /** @see org.apache.fop.layoutmgr.LayoutManager#addChildArea(org.apache.fop.area.Area) */ + public void addChildArea(Area childArea) { + int floatProperty = ((Float) getFObj()).getFloat(); + if (floatProperty == EN_BEFORE) { + childArea.setAreaClass(Area.CLASS_BEFORE_FLOAT); + } else if (floatProperty == EN_START || floatProperty == EN_END) { + childArea.setAreaClass(Area.CLASS_SIDE_FLOAT); + } else { + childArea.setAreaClass(Area.CLASS_NORMAL); + } + parentLM.addChildArea(childArea); + } +} Index: src/java/org/apache/fop/layoutmgr/InlineKnuthSequence.java =================================================================== --- src/java/org/apache/fop/layoutmgr/InlineKnuthSequence.java (révision 423415) +++ src/java/org/apache/fop/layoutmgr/InlineKnuthSequence.java (copie de travail) @@ -147,6 +147,7 @@ // from prevBox to the new box KnuthInlineBox newBox = (KnuthInlineBox) getLast(); newBox.setFootnoteBodyLM(((KnuthInlineBox) prevBox).getFootnoteBodyLM()); + newBox.setFloatBodyLM(((KnuthInlineBox) prevBox).getFloatBodyLM()); } } Index: src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java =================================================================== --- src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java (révision 423415) +++ src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java (copie de travail) @@ -991,7 +991,7 @@ * @param line number of the line ending at the node's corresponding breakpoint * @param node the active node to add */ - protected void addNode(int line, KnuthNode node) { + public void addNode(int line, KnuthNode node) { int headIdx = line * 2; if (headIdx >= activeLines.length) { KnuthNode[] oldList = activeLines; @@ -1015,7 +1015,7 @@ * @param line number of the line ending at the node's corresponding breakpoint * @param node the node to deactivate */ - protected void removeNode(int line, KnuthNode node) { + public void removeNode(int line, KnuthNode node) { int headIdx = line * 2; KnuthNode n = getNode(line); if (n != node) { Index: src/java/org/apache/fop/fo/FONode.java =================================================================== --- src/java/org/apache/fop/fo/FONode.java (révision 423415) +++ src/java/org/apache/fop/fo/FONode.java (copie de travail) @@ -174,13 +174,14 @@ /** * Checks to make sure, during SAX processing of input document, that the - * incoming node is valid for the this (parent) node (e.g., checking to + * incoming node is a valid child of this node (e.g., checking to * see that fo:table is not an immediate child of fo:root) * called within FObj constructor * @param loc location in the FO source file * @param namespaceURI namespace of incoming node - * @param localName (e.g. "table" for "fo:table") - * @throws ValidationException if incoming node not valid for parent + * @param localName name of the incoming node (e.g. "table" for "fo:table") + * @throws ValidationException if incoming node is note a valid child of + * this node */ protected void validateChildNode(Locator loc, String namespaceURI, String localName) throws ValidationException { Index: src/java/org/apache/fop/fo/flow/Float.java =================================================================== --- src/java/org/apache/fop/fo/flow/Float.java (révision 423415) +++ src/java/org/apache/fop/fo/flow/Float.java (copie de travail) @@ -36,20 +36,23 @@ private int clear; // End of property values - static boolean notImplementedWarningGiven = false; /** * @see org.apache.fop.fo.FONode#FONode(FONode) */ public Float(FONode parent) { super(parent); - - if (!notImplementedWarningGiven) { - getLogger().warn("fo:float is not yet implemented."); - notImplementedWarningGiven = true; - } } + + public int getClear() { + return clear; + } + + public int getFloat() { + return float_; + } + /** * @see org.apache.fop.fo.FObj#bind(PropertyList) */ Index: src/java/org/apache/fop/area/BeforeFloat.java =================================================================== --- src/java/org/apache/fop/area/BeforeFloat.java (révision 423415) +++ src/java/org/apache/fop/area/BeforeFloat.java (copie de travail) @@ -53,17 +53,22 @@ * * @return the height of the before float including separator */ - public int getBPD() { - int h = super.getBPD(); - if (separator != null) { - h += separator.getBPD(); - } - return h; - } +// public int getBPD() { +// int h = super.getBPD(); +// if (separator != null) { +// h += separator.getBPD(); +// } +// return h; +// } - /** @see org.apache.fop.area.BlockParent#isEmpty() */ - public boolean isEmpty() { - return true; // before floats are not yet implemented + /** + * Add a block area as child to the footnote area + * + * @param child the block area. + */ + public void addBlock(Block child) { + addChildArea(child); + this.setBPD(this.getBPD() + child.getBPD()); } }