*
* @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());
}
}
/**
* 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 {
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_;
}
/**
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;
}
}
private int alignmentLast;
protected MinOptMax footnoteSeparatorLength = new MinOptMax(0);
+ protected MinOptMax floatSeparatorLength = new MinOptMax(0);
protected abstract int getCurrentDisplayAlign();
protected abstract boolean hasMoreContent();
+ "), flow BPD =" + flowBPD);
PageBreakingAlgorithm alg = new PageBreakingAlgorithm(getTopLevelLM(),
getPageProvider(),
- alignment, alignmentLast, footnoteSeparatorLength,
+ alignment, alignmentLast, footnoteSeparatorLength, floatSeparatorLength,
isPartOverflowRecoveryActivated(), autoHeight, isSinglePartFavored());
int iOptPageCount;
PageSequenceLayoutManager.PageProvider pageProvider,\r
int alignment, int alignmentLast,\r
MinOptMax footnoteSeparatorLength,\r
+ MinOptMax floatSeparatorLength,\r
boolean partOverflowRecovery,\r
int columnCount) {\r
super(topLevelLM, pageProvider, alignment, alignmentLast, \r
- footnoteSeparatorLength, partOverflowRecovery, false, false);\r
+ footnoteSeparatorLength, floatSeparatorLength, partOverflowRecovery, false, false);\r
this.columnCount = columnCount;\r
this.considerTooShort = true; //This is important!\r
}\r
* @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;
* @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) {
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import java.util.LinkedList;
+
+import org.apache.fop.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);
+ }
+}
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;
// from prevBox to the new box
KnuthInlineBox newBox = (KnuthInlineBox) getLast();
newBox.setFootnoteBodyLM(((KnuthInlineBox) prevBox).getFootnoteBodyLM());
+ newBox.setFloatBodyLM(((KnuthInlineBox) prevBox).getFloatBodyLM());
}
}
* 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.
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);
+ }
+
+ /**
+ * @return true if this box contains float citations.
+ */
+ 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();
+ }
+ footnoteElementLists.add(list);
+ }
+
+ /**
+ * Returns the list of footnotes cited by this box.
+ * @return a list of KnuthElement sequences corresponding to the footnote bodies
+ */
+ public LinkedList getFootnoteElementLists() {
+ return footnoteElementLists;
}
/**
- * Adds the given list of Knuth elements to this box' list of elements.
- * @param list elements corresponding to a footnote body
+ * Adds a float to this box's list of floats.
+ * @param list KnuthElement instances corresponding to the float body
*/
- public void addElementList(LinkedList list) {
- if (elementLists == null) {
- elementLists = new LinkedList();
+ public void addFloatElementList(LinkedList list) {
+ if (floatElementLists == null) {
+ floatElementLists = new LinkedList();
}
- elementLists.add(list);
+ floatElementLists.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 floats cited by this box.
+ * @return a list of KnuthElement sequences corresponding to the float bodies
*/
- public LinkedList getElementLists() {
- return elementLists;
+ public LinkedList getFloatElementLists() {
+ return floatElementLists;
}
/**
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;
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;
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;
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());
}
}
+ 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));
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;
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);
/** 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
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);
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;
* This class represents a feasible breaking point
* with extra information about footnotes.
*/
- protected class KnuthPageNode extends KnuthNode {
-
- /** Additional length due to footnotes. */
- public int totalFootnotes;
-
- /** Index of the last inserted footnote. */
- public int footnoteListIndex;
+ public class KnuthPageNode extends KnuthNode {
- /** Index of the last inserted element of the last inserted footnote. */
- public int footnoteElementIndex;
+ public OutOfLineRecord.ProgressInfo footnotesProgress;
+ public OutOfLineRecord.ProgressInfo floatsProgress;
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();
}
}
*/
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);
}
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));
*/
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 (!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;
+ if (box instanceof KnuthBlockBox
+ && ((KnuthBlockBox) box).hasFloatAnchors()) {
+ floats.add(((KnuthBlockBox) box).getFloatElementLists());
}
}
+
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
+ }
+ 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 {
- // all footnotes have already been placed on previous pages
}
- } 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 <code>true</code> 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
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 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;
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();
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;
}
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
*/
}
}
// 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
}
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));
}
}
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();
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;
private int startPageNum = 0;
private int currentPageNum = 0;
- private Block separatorArea = null;
+ private Block footnoteSeparatorArea = null;
+ private Block floatSeparatorArea = null;
/**
* Constructor
private boolean needColumnBalancing;
private StaticContentLayoutManager footnoteSeparatorLM = null;
+ private StaticContentLayoutManager floatSeparatorLM = null;
public PageBreaker(PageSequenceLayoutManager pslm) {
this.pslm = pslm;
}
// 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
// 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;
}
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);
}
getTopLevelLM(),
getPageProvider(),
alg.getAlignment(), alg.getAlignmentLast(),
- footnoteSeparatorLength,
+ footnoteSeparatorLength, floatSeparatorLength,
isPartOverflowRecoveryActivated(), false, false);
//alg.setConstantLineWidth(flowBPD);
int iOptPageCount = algRestart.findBreakingPoints(effectiveList,
PageBreakingAlgorithm algRestart = new BalancingColumnBreakingAlgorithm(
getTopLevelLM(),
getPageProvider(),
- alignment, Constants.EN_START, footnoteSeparatorLength,
+ alignment, Constants.EN_START, footnoteSeparatorLength, floatSeparatorLength,
isPartOverflowRecoveryActivated(),
getCurrentPV().getBodyRegion().getColumnCount());
//alg.setConstantLineWidth(flowBPD);
}
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) {
}
// 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();
}
* @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);
* @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;
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) {
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr.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 <code>true</code> 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 <code>true</code> 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 <code>null</code> 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);
+ }
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr.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;
+ }
+}
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;
public class KnuthInlineBox extends KnuthBox {
private FootnoteBodyLayoutManager footnoteBodyLM = null;
+ private FloatBodyLayoutManager floatBodyLM = null;
private AlignmentContext alignmentContext = null;
/**
}
/**
- * @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 <code>true</code> if this box holds a reference to a footnote
+ */
+ public boolean isFootnoteAnchor() {
return (footnoteBodyLM != null);
}
-
-
+
+ /**
+ * @return <code>true</code> 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
+}
// 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;
= (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) {
<changes>
<release version="FOP Trunk">
+ <action context="Code" dev="JM" type="add" fixes-bug="39777" due-to="Vincent Hennebert">
+ Initial support for fo:float with float="before" and float="none".
+ </action>
<action context="Code" dev="JM" type="fix" fixes-bug="40106" due-to="Jeroen Meijer">
Compatibility fix for GCJ (GNU Classpath): Using "UTF-16BE" instead of "UnicodeBigUnmarked"
encoding.
<description>Long text cause an IndexOutOfBounds exception</description>
<reference>http://issues.apache.org/bugzilla/show_bug.cgi?id=39414</reference>
</testcase>
-</disabled-testcases>
\ No newline at end of file
+ <testcase>
+ <name>fo:float float="none" should be split</name>
+ <file>float_float_none_2.xml</file>
+ <description>When float="none", it should be possible to split an fo:float
+ element on several pages.</description>
+ </testcase>
+ <testcase>
+ <name>Before-floats when no stretch available</name>
+ <file>before-float_not-deferred_no-stretch_2.xml</file>
+ <description>Whereas there is room for placing the before-float on the same
+ page as its citation, the algorithm prefers to defer it to the following
+ page.</description>
+ </testcase>
+ <testcase>
+ <name>Before-floats deferred when no stretch available</name>
+ <file>before-float_deferred_no-stretch.xml</file>
+ <description>Instead of deferring a before-float on the following page, an
+ underfull page is created so that the citation be on the same page as the
+ float.</description>
+ </testcase>
+</disabled-testcases>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks that a before-float on a simple one-page document is
+ rightly placed on the top of the page.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="3in">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="normal" white-space-collapse="true">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ <fo:block>
+ This is a block with a float. This is a block with a float.
+ The float anchor is just behind this <fo:inline
+ color="blue">word</fo:inline><fo:float float="before"
+ color="red">
+ <fo:block>
+ This is the float content. This is the float content. This is
+ the float content. This is the float content. This is the float
+ content. This is the float content. This is the float content.
+ This is the float content.
+ </fo:block>
+ </fo:float>.
+ This is a block with a float. This is a block with a float.
+ This is a block with a float. This is a block with a float.
+ </fo:block>
+ <fo:block>
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="1" xpath="count(//pageViewport)"/>
+ <!-- before-float block -->
+ <eval expected="1"
+ xpath="count(//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block)"/>
+ <eval expected="This is the float content. This is the float content. This is the float"
+ xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block/lineArea/text[1]"/>
+ <eval expected="43200"
+ xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block/@bpd"/>
+ <!-- main area -->
+ <eval expected="3"
+ xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block)"/>
+ </checks>
+</testcase>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ Test for before-floats. This a complex documents with several floats; some
+ may be put on the same page as their citation, others not.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="3in">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="normal" white-space-collapse="true">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block space-after.minimum="2pt"
+ space-after.optimum="6pt"
+ space-after.maximum="12pt"
+ orphans="1"
+ widows="1">
+ <fo:block space-after="inherit">
+ This is a block with a float. This is a block with a float.
+ This float has the number <fo:inline
+ color="blue">1</fo:inline><fo:float float="before" color="red">
+ <fo:block>
+ This is the float content. This is the float content. This is
+ the float content. This is the float content. This is the
+ float content. This was the float number 1.
+ </fo:block>
+ </fo:float>.
+ This is a block with a float. This is a block with a float.
+ This is a block with a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block with a float. This is a block with a float.
+ This float has the number <fo:inline
+ color="blue">2</fo:inline><fo:float float="before" color="red">
+ <fo:block>
+ This is the float content. This is the float content. This is
+ the float content. This is the float content. This is the
+ float content. This was the float number 2.
+ </fo:block>
+ </fo:float>.
+ This is a block with a float. This is a block with a float.
+ This is a block with a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block with a float. This is a block with a float.
+ This float has the number <fo:inline
+ color="blue">3</fo:inline><fo:float float="before" color="red">
+ <fo:block>
+ This is the float content. This is the float content. This is
+ the float content. This is the float content. This is the
+ float content. This was the float number 3.
+ </fo:block>
+ </fo:float>.
+ This is a block with a float. This is a block with a float.
+ This is a block with a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block with a float. This is a block with a float.
+ This float has the number <fo:inline
+ color="blue">4</fo:inline><fo:float float="before" color="red">
+ <fo:block>
+ This is the float content. This is the float content. This is
+ the float content. This is the float content. This is the
+ float content. This was the float number 4.
+ </fo:block>
+ </fo:float>.
+ This is a block with a float. This is a block with a float.
+ This is a block with a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block with a float. This is a block with a float.
+ This float has the number <fo:inline
+ color="blue">5</fo:inline><fo:float float="before" color="red">
+ <fo:block>
+ This is the float content. This is
+ the float content. This is the float content. This is the
+ float content. This is the float content. This is the float
+ content. This is the float content. This is the float content.
+ This is the float content. This is the float content. This is
+ the float content. This is the float content. This is the
+ float content. This is the float content. This is the float
+ content. This is the float content. This is the float content.
+ This was the float number 5.
+ </fo:block>
+ </fo:float>.
+ This is a block with a float. This is a block with a float.
+ This is a block with a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="5" xpath="count(//pageViewport)"/>
+ <!-- page 1 -->
+ <eval expected="2"
+ xpath="count(//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block)"/>
+ <!-- float number 1 -->
+ <eval expected="the float number 1."
+ xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block[1]/lineArea[3]/text"/>
+ <eval expected="43200"
+ xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block[1]/@bpd"/>
+ <!-- float number 2 -->
+ <eval expected="the float number 2."
+ xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block[2]/lineArea[3]/text"/>
+ <eval expected="43200"
+ xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block[2]/@bpd"/>
+ <!-- page 2 -->
+ <eval expected="1"
+ xpath="count(//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block)"/>
+ <!-- float number 3 -->
+ <eval expected="the float number 3."
+ xpath="//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block/lineArea[3]/text"/>
+ <eval expected="43200"
+ xpath="//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block/@bpd"/>
+ <!-- page 3 -->
+ <eval expected="1"
+ xpath="count(//pageViewport[3]/page/regionViewport/regionBody/beforeFloat/block)"/>
+ <!-- float number 4 -->
+ <eval expected="the float number 4."
+ xpath="//pageViewport[3]/page/regionViewport/regionBody/beforeFloat/block/lineArea[3]/text"/>
+ <eval expected="43200"
+ xpath="//pageViewport[3]/page/regionViewport/regionBody/beforeFloat/block/@bpd"/>
+ <!-- page 4 -->
+ <eval expected="1"
+ xpath="count(//pageViewport[4]/page/regionViewport/regionBody/beforeFloat/block)"/>
+ <!-- float number 5 -->
+ <eval expected="This is the float content. This was the float number 5."
+ xpath="//pageViewport[4]/page/regionViewport/regionBody/beforeFloat/block/lineArea[7]/text"/>
+ <eval expected="100800"
+ xpath="//pageViewport[4]/page/regionViewport/regionBody/beforeFloat/block/@bpd"/>
+ <!-- page 5 -->
+ <eval expected="0"
+ xpath="count(//pageViewport[5]/page/regionViewport/regionBody/beforeFloat/block)"/>
+ </checks>
+</testcase>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ Test for before-floats. On a two-page document, when no stretching is
+ allowed between blocks, when the float citation is on the first page and
+ there is not enough room for the float, it should be placed on the top of
+ the second page.
+ </p>
+ <p>
+ When no stretching is available, only "too short" nodes are created. When
+ there is not enough room to place the float, a too short node is created
+ with added demerits for the deferred float. This results in higher
+ demerits than for previous too short nodes, which then are preferred
+ whereas they create underfull pages.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="3.1in">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="normal" white-space-collapse="true">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block orphans="1" widows="1">
+ <fo:block>
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ <fo:block>
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ <fo:block>
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ <fo:block>
+ This is a block with a float. This is a block with a float.
+ The float anchor is just behind this <fo:inline
+ color="blue">word</fo:inline><fo:float float="before"
+ color="red">
+ <fo:block>
+ This is the float content. This is the float content. This is
+ the float content. This is the float content. This is the
+ float content. This is the float content. This is the float
+ content. This is the float content.
+ </fo:block>
+ </fo:float>.
+ This is a block with a float. This is a block with a float.
+ This is a block with a float. This is a block with a float.
+ This is a block with a float. This is a block with a float.
+ This is a block with a float. This is a block with a float.
+ </fo:block>
+ <fo:block>
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="2" xpath="count(//pageViewport)"/>
+ <!-- first page -->
+ <eval expected="0"
+ xpath="count(//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block)"/>
+ <!-- main area -->
+ <eval expected="4"
+ xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block/block)"/>
+ <!-- four lines in the last block -->
+ <eval expected="4"
+ xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block/block[4]/lineArea)"/>
+ <!-- second page -->
+ <!-- before-float block -->
+ <eval expected="1"
+ xpath="count(//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block)"/>
+ <eval expected="This is the float content. This is the float content. This is the float"
+ xpath="//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block/lineArea/text[1]"/>
+ <eval expected="43200"
+ xpath="//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block/@bpd"/>
+ <!-- one block in the normal content -->
+ <eval expected="2"
+ xpath="count(//pageViewport[2]/page/regionViewport/regionBody/mainReference/span/flow/block/block)"/>
+ <!-- one remaining line in the first block -->
+ <eval expected="1"
+ xpath="count(//pageViewport[2]/page/regionViewport/regionBody/mainReference/span/flow/block/block[1]/lineArea)"/>
+ </checks>
+</testcase>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ Test for before-floats. On a two-page document, when some stretching is
+ allowed between blocks, when the float citation is on the first page and
+ there is not enough room for the float, it should be placed on the top of
+ the second page.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="3in">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="normal" white-space-collapse="true">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block space-after.minimum="2pt"
+ space-after.optimum="6pt"
+ space-after.maximum="16pt"
+ orphans="1"
+ widows="1">
+ <fo:block space-after="inherit">
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block with a float. This is a block with a float.
+ The float anchor is just behind this <fo:inline
+ color="blue">word</fo:inline><fo:float float="before"
+ color="red">
+ <fo:block>
+ This is the float content. This is the float content. This is
+ the float content. This is the float content. This is the
+ float content. This is the float content. This is the float
+ content. This is the float content.
+ </fo:block>
+ </fo:float>.
+ This is a block with a float. This is a block with a float.
+ This is a block with a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="2" xpath="count(//pageViewport)"/>
+ <!-- first page -->
+ <eval expected="0"
+ xpath="count(//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block)"/>
+ <!-- main area -->
+ <eval expected="4"
+ xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block/block)"/>
+ <!-- only one line in the last block -->
+ <eval expected="1"
+ xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block/block[4]/lineArea)"/>
+ <!-- second page -->
+ <!-- before-float block -->
+ <eval expected="1"
+ xpath="count(//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block)"/>
+ <eval expected="This is the float content. This is the float content. This is the float"
+ xpath="//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block/lineArea/text[1]"/>
+ <eval expected="43200"
+ xpath="//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block/@bpd"/>
+ <!-- one block in the normal content -->
+ <eval expected="1"
+ xpath="count(//pageViewport[2]/page/regionViewport/regionBody/mainReference/span/flow/block/block)"/>
+ <!-- three remaining lines in the normal content -->
+ <eval expected="3"
+ xpath="count(//pageViewport[2]/page/regionViewport/regionBody/mainReference/span/flow/block/block[1]/lineArea)"/>
+ </checks>
+</testcase>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ Test for before-floats. On a two-page document, when no stretching is
+ allowed between blocks, when the float citation is on the first page and
+ there is enough room for the float, it should be placed on the top of the
+ first page.
+ </p>
+ <p>
+ When no stretching is available, only "too short" nodes are created. There
+ is no feasible break. The result regarding floats should be the same,
+ however.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="3.1in">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="normal" white-space-collapse="true">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>
+ This is a block with a float. This is a block with a float.
+ The float anchor is just behind this <fo:inline
+ color="blue">word</fo:inline><fo:float float="before"
+ color="red">
+ <fo:block>
+ This is the float content. This is the float content. This is
+ the float content. This is the float content. This is the float
+ content. This is the float content. This is the float content.
+ This is the float content.
+ </fo:block>
+ </fo:float>.
+ This is a block with a float. This is a block with a float.
+ This is a block with a float. This is a block with a float.
+ </fo:block>
+ <fo:block>
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="2" xpath="count(//pageViewport)"/>
+ <!-- first page -->
+ <!-- before-float block -->
+ <eval expected="1"
+ xpath="count(//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block)"/>
+ <eval expected="This is the float content. This is the float content. This is the float"
+ xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block/lineArea/text[1]"/>
+ <eval expected="43200"
+ xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block/@bpd"/>
+ <!-- main area -->
+ <eval expected="2"
+ xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block)"/>
+ <!-- only eight lines in the last block -->
+ <eval expected="8"
+ xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block[2]/lineArea)"/>
+ <!-- second page -->
+ <eval expected="0"
+ xpath="count(//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block)"/>
+ <!-- four remaining lines from the previous last block -->
+ <eval expected="4"
+ xpath="count(//pageViewport[2]/page/regionViewport/regionBody/mainReference/span/flow/block[1]/lineArea)"/>
+ </checks>
+</testcase>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test does not work. There is a before-float and a block of normal
+ text with no stretching. Normally the float should be on the first page.
+ </p>
+ <p>
+ Without the before-float, the normal content would fit on one page. With
+ the float, two pages are necessary. As there is no available stretching
+ the algorithm can only create "too short" nodes. In such a case it prefers
+ to defer the float on the last page, instead of putting it on the first
+ page and splitting the normal content on two pages. This should be
+ corrected eventually.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="3.1in">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="normal" white-space-collapse="true">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>
+ This is a block with a float. This is a block with a float.
+ The float anchor is just behind this <fo:inline
+ color="blue">word</fo:inline><fo:float float="before"
+ color="red">
+ <fo:block>
+ This is the float content. This is the float content. This is
+ the float content. This is the float content. This is the float
+ content. This is the float content. This is the float content.
+ This is the float content.
+ </fo:block>
+ </fo:float>.
+ This is a block with a float. This is a block with a float.
+ This is a block with a float. This is a block with a float.
+ </fo:block>
+ <fo:block>
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="2" xpath="count(//pageViewport)"/>
+ <!-- first page -->
+ <!-- before-float block -->
+ <eval expected="1"
+ xpath="count(//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block)"/>
+ <eval expected="This is the float content. This is the float content. This is the float"
+ xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block/lineArea/text[1]"/>
+ <eval expected="43200"
+ xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block/@bpd"/>
+ <!-- main area -->
+ <eval expected="2"
+ xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block)"/>
+ <!-- second page -->
+ <eval expected="0"
+ xpath="count(//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block)"/>
+ <!-- three remaining lines on the second page -->
+ <eval expected="3"
+ xpath="count(//pageViewport[2]/page/regionViewport/regionBody/mainReference/span/flow/block[1]/lineArea)"/>
+ </checks>
+</testcase>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ Test for before-floats. On a two-page document, when some stretching is
+ allowed between blocks, when the float citation is on the first page and
+ there is enough room for the float, it should be placed on the top of the
+ first page.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="3in">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="normal" white-space-collapse="true">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block space-after.minimum="6pt"
+ space-after.optimum="10pt"
+ space-after.maximum="16pt"
+ orphans="1"
+ widows="1">
+ <fo:block space-after="inherit">
+ This is a block with a float. This is a block with a float.
+ The float anchor is just behind this <fo:inline
+ color="blue">word</fo:inline><fo:float float="before"
+ color="red">
+ <fo:block>
+ This is the float content. This is the float content. This is
+ the float content. This is the float content. This is the
+ float content. This is the float content. This is the float
+ content. This is the float content.
+ </fo:block>
+ </fo:float>.
+ This is a block with a float. This is a block with a float.
+ This is a block with a float. This is a block with a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ This is a block without a float. This is a block without a float.
+ </fo:block>
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="2" xpath="count(//pageViewport)"/>
+ <!-- first page -->
+ <!-- before-float block -->
+ <eval expected="1"
+ xpath="count(//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block)"/>
+ <eval expected="This is the float content. This is the float content. This is the float"
+ xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block/lineArea/text[1]"/>
+ <eval expected="43200"
+ xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block/@bpd"/>
+ <!-- main area -->
+ <eval expected="3"
+ xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block/block)"/>
+ <!-- only two lines in the last block -->
+ <eval expected="2"
+ xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block/block[3]/lineArea)"/>
+ <!-- second page -->
+ <eval expected="0"
+ xpath="count(//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block)"/>
+ </checks>
+</testcase>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks that fo:float objects with a "float" property of
+ "none" are typeset in the normal flow of elements.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="3in">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="normal" white-space-collapse="true">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block space-after.minimum="6pt" space-after.optimum="10pt"
+ space-after.maximum="12pt">
+ <fo:block space-after="inherit">
+ This is a block containing no before-float. This is a block
+ containing no before-float. This is a block containing no
+ before-float. This is a block containing no before-float. This is
+ a block containing no before-float. This is a block containing no
+ before-float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block containing a before-float with "float" set to
+ "none". The float anchor is just behind this <fo:inline
+ color="blue">word</fo:inline><fo:float float="none" color="red">
+ <fo:block>
+ This is the float content. This is the float content. This is
+ the float content. This is the float content. This is the
+ float content. This is the float content. This is the float
+ content.
+ </fo:block>
+ </fo:float>
+ This is the end of the block containing the float. This is the
+ end of the block containing the float. This is the end of the
+ block containing the float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block containing no before-float. This is a block
+ containing no before-float. This is a block containing no
+ before-float. This is a block containing no before-float. This is
+ a block containing no before-float. This is a block containing no
+ before-float.
+ </fo:block>
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="2" xpath="count(//pageViewport)"/>
+ <!-- block with footnote -->
+ <eval expected='This is a block containing a before-float with "float" set to "none".'
+ xpath="//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block[1]/block[2]/lineArea[1]/text[1]"/>
+ <eval expected="This is the float content. This is the float content. This is the float"
+ xpath="//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block[1]/block[2]/block[1]/lineArea[1]/text[1]"/>
+ <eval expected="43200"
+ xpath="//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block[1]/block[2]/block[1]/@bpd"/>
+ </checks>
+</testcase>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <!-- don't work, not implemented, TODO -->
+ <p>
+ This test checks that fo:float objects with a "float" property of
+ "none" are typeset in the normal flow of elements, and normally broken
+ across pages.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="2in">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="normal" white-space-collapse="true">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block space-after.minimum="6pt" space-after.optimum="10pt"
+ space-after.maximum="12pt">
+ <fo:block space-after="inherit">
+ This is a block containing no fo:float. This is a block
+ containing no fo:float. This is a block containing no fo:float.
+ This is a block containing no fo:float. This is a block
+ containing no fo:float. This is a block containing no fo:float.
+ This is a block containing no fo:float. This is a block
+ containing no fo:float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block containing an fo:float with "float" set to
+ "none", which will be split on two pages. The float anchor is
+ just behind this <fo:inline
+ color="blue">word</fo:inline><fo:float float="none" color="red">
+ <fo:block>
+ This is the float content. This is the float content. This is
+ the float content. This is the float content. This is the
+ float content. This is the float content. This is the float
+ content.
+ </fo:block>
+ </fo:float>
+ This is the end of the block containing the float. This is the
+ end of the block containing the float. This is the end of the
+ block containing the float.
+ </fo:block>
+ <fo:block space-after="inherit">
+ This is a block containing no fo:float. This is a block
+ containing no fo:float. This is a block containing no fo:float.
+ This is a block containing no fo:float. This is a block
+ containing no fo:float. This is a block containing no fo:float.
+ </fo:block>
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="2" xpath="count(//pageViewport)"/>
+ <eval expected='This is a block containing a before-float with "float" set to "none".'
+ xpath="//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block[1]/block[2]/lineArea[1]/text[1]"/>
+ <eval expected="This is the float content. This is the float content. This is the float"
+ xpath="//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block[1]/block[2]/block[1]/lineArea[1]/text[1]"/>
+ <eval expected="43200"
+ xpath="//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block[1]/block[2]/block[1]/@bpd"/>
+ </checks>
+</testcase>