blob: ba676ab897c7f5b90cc61f3029dd9d3f3d0fd7f7 [file] [log] [blame]
/*
* 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.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import org.apache.fop.area.Block;
import org.apache.fop.area.BodyRegion;
import org.apache.fop.area.Footnote;
import org.apache.fop.area.PageViewport;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.pagination.Region;
import org.apache.fop.fo.pagination.RegionBody;
import org.apache.fop.fo.pagination.StaticContent;
import org.apache.fop.layoutmgr.BreakingAlgorithm.KnuthNode;
import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener;
import org.apache.fop.layoutmgr.list.ListItemLayoutManager;
import org.apache.fop.traits.MinOptMax;
/**
* Handles the breaking of pages in an fo:flow
*/
public class PageBreaker extends AbstractBreaker {
private PageSequenceLayoutManager pslm;
private boolean firstPart = true;
private boolean pageBreakHandled;
private boolean needColumnBalancing;
private PageProvider pageProvider;
private Block separatorArea;
private boolean spanAllActive;
private boolean handlingStartOfFloat;
private boolean handlingEndOfFloat;
private int floatHeight;
private int floatYOffset;
private List relayedFootnotesList;
private List relayedLengthList;
private int relayedTotalFootnotesLength;
private int relayedInsertedFootnotesLength;
private boolean relayedFootnotesPending;
private boolean relayedNewFootnotes;
private int relayedFirstNewFootnoteIndex;
private int relayedFootnoteListIndex;
private int relayedFootnoteElementIndex = -1;
private MinOptMax relayedFootnoteSeparatorLength;
private int previousFootnoteListIndex = -2;
private int previousFootnoteElementIndex = -2;
/**
* The FlowLayoutManager object, which processes
* the single fo:flow of the fo:page-sequence
*/
private FlowLayoutManager childFLM;
private StaticContentLayoutManager footnoteSeparatorLM;
/**
* Construct page breaker.
* @param pslm the page sequence layout manager
*/
public PageBreaker(PageSequenceLayoutManager pslm) {
this.pslm = pslm;
this.pageProvider = pslm.getPageProvider();
this.childFLM = pslm.getLayoutManagerMaker().makeFlowLayoutManager(
pslm, pslm.getPageSequence().getMainFlow());
}
/** {@inheritDoc} */
protected void updateLayoutContext(LayoutContext context) {
int flowIPD = pslm.getCurrentColumnWidth();
context.setRefIPD(flowIPD);
}
/** {@inheritDoc} */
protected LayoutManager getTopLevelLM() {
return pslm;
}
/** {@inheritDoc} */
protected PageProvider getPageProvider() {
return pslm.getPageProvider();
}
/**
* Starts the page breaking process.
* @param flowBPD the constant available block-progression-dimension (used for every part)
*/
void doLayout(int flowBPD) {
doLayout(flowBPD, false);
}
/** {@inheritDoc} */
protected PageBreakingLayoutListener createLayoutListener() {
return new PageBreakingLayoutListener() {
public void notifyOverflow(int part, int amount, FObj obj) {
Page p = pageProvider.getPageFromColumnIndex(part);
RegionBody body = (RegionBody)p.getSimplePageMaster().getRegion(
Region.FO_REGION_BODY);
BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
body.getUserAgent().getEventBroadcaster());
boolean canRecover = (body.getOverflow() != Constants.EN_ERROR_IF_OVERFLOW);
boolean needClip = (body.getOverflow() == Constants.EN_HIDDEN
|| body.getOverflow() == Constants.EN_ERROR_IF_OVERFLOW);
eventProducer.regionOverflow(this, body.getName(),
p.getPageViewport().getPageNumberString(),
amount, needClip, canRecover,
body.getLocator());
}
};
}
/** {@inheritDoc} */
protected int handleSpanChange(LayoutContext childLC, int nextSequenceStartsOn) {
needColumnBalancing = false;
if (childLC.getNextSpan() != Constants.NOT_SET) {
//Next block list will have a different span.
nextSequenceStartsOn = childLC.getNextSpan();
needColumnBalancing = childLC.getNextSpan() == Constants.EN_ALL
&& childLC.getDisableColumnBalancing() == Constants.EN_FALSE;
}
if (needColumnBalancing) {
AbstractBreaker.log.debug(
"Column balancing necessary for the next element list!!!");
}
return nextSequenceStartsOn;
}
/** {@inheritDoc} */
protected int getNextBlockList(LayoutContext childLC,
int nextSequenceStartsOn) {
return getNextBlockList(childLC, nextSequenceStartsOn, null, null, null);
}
/** {@inheritDoc} */
protected int getNextBlockList(LayoutContext childLC, int nextSequenceStartsOn,
Position positionAtIPDChange, LayoutManager restartLM, List firstElements) {
if (!handlingFloat()) {
if (!firstPart) {
// if this is the first page that will be created by
// the current BlockSequence, it could have a break
// condition that must be satisfied;
// otherwise, we may simply need a new page
handleBreakTrait(nextSequenceStartsOn);
}
firstPart = false;
pageBreakHandled = true;
pageProvider.setStartOfNextElementList(pslm.getCurrentPageNum(), pslm.getCurrentPV()
.getCurrentSpan().getCurrentFlowIndex(), this.spanAllActive);
}
return super.getNextBlockList(childLC, nextSequenceStartsOn, positionAtIPDChange,
restartLM, firstElements);
}
private boolean containsFootnotes(List contentList, LayoutContext context) {
boolean containsFootnotes = 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
containsFootnotes = true;
KnuthBlockBox box = (KnuthBlockBox) element;
List<List<KnuthElement>> footnotes = getFootnoteKnuthElements(childFLM, context,
box.getFootnoteBodyLMs());
for (List<KnuthElement> footnote : footnotes) {
box.addElementList(footnote);
}
}
}
}
return containsFootnotes;
}
public static List<List<KnuthElement>> getFootnoteKnuthElements(FlowLayoutManager flowLM, LayoutContext context,
List<FootnoteBodyLayoutManager> footnoteBodyLMs) {
List<List<KnuthElement>> footnotes = new ArrayList<List<KnuthElement>>();
LayoutContext footnoteContext = LayoutContext.copyOf(context);
footnoteContext.setStackLimitBP(context.getStackLimitBP());
footnoteContext.setRefIPD(flowLM.getPSLM()
.getCurrentPV().getRegionReference(Constants.FO_REGION_BODY).getIPD());
for (FootnoteBodyLayoutManager fblm : footnoteBodyLMs) {
fblm.setParent(flowLM);
fblm.initialize();
List<KnuthElement> footnote = fblm.getNextKnuthElements(footnoteContext, Constants.EN_START);
// TODO this does not respect possible stacking constraints between footnotes
SpaceResolver.resolveElementList(footnote);
footnotes.add(footnote);
}
return footnotes;
}
private void handleFootnoteSeparator() {
StaticContent footnoteSeparator;
footnoteSeparator = pslm.getPageSequence().getStaticContent("xsl-footnote-separator");
if (footnoteSeparator != null) {
// the footnote 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
separatorArea = new Block();
separatorArea.setIPD(pslm.getCurrentPV()
.getRegionReference(Constants.FO_REGION_BODY).getIPD());
// create a StaticContentLM for the footnote separator
footnoteSeparatorLM
= pslm.getLayoutManagerMaker().makeStaticContentLayoutManager(
pslm, footnoteSeparator, separatorArea);
footnoteSeparatorLM.doLayout();
footnoteSeparatorLength = MinOptMax.getInstance(separatorArea.getBPD());
}
}
/** {@inheritDoc} */
protected List getNextKnuthElements(LayoutContext context, int alignment) {
List contentList = null;
while (!childFLM.isFinished() && contentList == null) {
contentList = childFLM.getNextKnuthElements(context, alignment);
}
// scan contentList, searching for footnotes
if (containsFootnotes(contentList, context)) {
// handle the footnote separator
handleFootnoteSeparator();
}
return contentList;
}
/** {@inheritDoc} */
protected List getNextKnuthElements(LayoutContext context, int alignment,
Position positionAtIPDChange, LayoutManager restartAtLM) {
List contentList = null;
do {
contentList = childFLM.getNextKnuthElements(context, alignment, positionAtIPDChange,
restartAtLM);
} while (!childFLM.isFinished() && contentList == null);
// scan contentList, searching for footnotes
if (containsFootnotes(contentList, context)) {
// handle the footnote separator
handleFootnoteSeparator();
}
return contentList;
}
/**
* @return current display alignment
*/
protected int getCurrentDisplayAlign() {
return pslm.getCurrentPage().getSimplePageMaster().getRegion(
Constants.FO_REGION_BODY).getDisplayAlign();
}
/**
* @return whether or not this flow has more page break opportunities
*/
protected boolean hasMoreContent() {
return !childFLM.isFinished();
}
/**
* Adds an area to the flow layout manager
* @param posIter the position iterator
* @param context the layout context
*/
protected void addAreas(PositionIterator posIter, LayoutContext context) {
if (footnoteSeparatorLM != null) {
StaticContent footnoteSeparator = pslm.getPageSequence().getStaticContent(
"xsl-footnote-separator");
// create a Block area that will contain the separator areas
separatorArea = new Block();
separatorArea.setIPD(
pslm.getCurrentPV().getRegionReference(Constants.FO_REGION_BODY).getIPD());
// create a StaticContentLM for the footnote separator
footnoteSeparatorLM = pslm.getLayoutManagerMaker().makeStaticContentLayoutManager(
pslm, footnoteSeparator, separatorArea);
footnoteSeparatorLM.doLayout();
}
childFLM.addAreas(posIter, context);
}
/**
* {@inheritDoc}
* This implementation checks whether to trigger column-balancing,
* or whether to take into account a 'last-page' condition.
*/
protected void doPhase3(PageBreakingAlgorithm alg, int partCount,
BlockSequence originalList, BlockSequence effectiveList) {
if (needColumnBalancing) {
//column balancing for the last part
redoLayout(alg, partCount, originalList, effectiveList);
return;
}
boolean lastPageMasterDefined = pslm.getPageSequence().hasPagePositionLast()
|| pslm.getPageSequence().hasPagePositionOnly() && pslm.isOnFirstPage(partCount - 1);
if (!hasMoreContent()) {
//last part is reached
if (lastPageMasterDefined) {
//last-page condition
redoLayout(alg, partCount, originalList, effectiveList);
return;
}
}
//nothing special: just add the areas now
addAreas(alg, partCount, originalList, effectiveList);
}
/**
* Restart the algorithm at the break corresponding to the given partCount. Used to
* re-do the part after the last break in case of either column-balancing or a last
* page-master.
*/
private void redoLayout(PageBreakingAlgorithm alg, int partCount,
BlockSequence originalList, BlockSequence effectiveList) {
int newStartPos = 0;
int restartPoint = pageProvider.getStartingPartIndexForLastPage(partCount);
if (restartPoint > 0) {
//Add definitive areas for the parts before the
//restarting point
addAreas(alg, restartPoint, originalList, effectiveList);
//Get page break from which we restart
PageBreakPosition pbp = alg.getPageBreaks().get(restartPoint - 1);
newStartPos = alg.par.getFirstBoxIndex(pbp.getLeafPos() + 1);
//Handle page break right here to avoid any side-effects
if (newStartPos > 0) {
handleBreakTrait(Constants.EN_PAGE);
}
}
AbstractBreaker.log.debug("Restarting at " + restartPoint
+ ", new start position: " + newStartPos);
pageBreakHandled = true;
//Update so the available BPD is reported correctly
int currentPageNum = pslm.getCurrentPageNum();
pageProvider.setStartOfNextElementList(currentPageNum,
pslm.getCurrentPV().getCurrentSpan().getCurrentFlowIndex(), this.spanAllActive);
//Make sure we only add the areas we haven't added already
effectiveList.ignoreAtStart = newStartPos;
PageBreakingAlgorithm algRestart;
if (needColumnBalancing) {
AbstractBreaker.log.debug("Column balancing now!!!");
AbstractBreaker.log.debug("===================================================");
//Restart last page
algRestart = new BalancingColumnBreakingAlgorithm(
getTopLevelLM(), getPageProvider(), createLayoutListener(),
alignment, Constants.EN_START, footnoteSeparatorLength,
isPartOverflowRecoveryActivated(),
pslm.getCurrentPV().getBodyRegion().getColumnCount());
AbstractBreaker.log.debug("===================================================");
} else {
// Handle special page-master for last page
BodyRegion currentBody = pageProvider.getPage(false, currentPageNum)
.getPageViewport().getBodyRegion();
setLastPageIndex(currentPageNum);
BodyRegion lastBody = pageProvider.getPage(false, currentPageNum)
.getPageViewport().getBodyRegion();
lastBody.getMainReference().setSpans(currentBody.getMainReference().getSpans());
AbstractBreaker.log.debug("Last page handling now!!!");
AbstractBreaker.log.debug("===================================================");
//Restart last page
algRestart = new PageBreakingAlgorithm(
getTopLevelLM(), getPageProvider(), createLayoutListener(),
alg.getAlignment(), alg.getAlignmentLast(),
footnoteSeparatorLength,
isPartOverflowRecoveryActivated(), false, false);
AbstractBreaker.log.debug("===================================================");
}
int optimalPageCount = algRestart.findBreakingPoints(effectiveList,
newStartPos,
1, true, BreakingAlgorithm.ALL_BREAKS);
AbstractBreaker.log.debug("restart: optimalPageCount= " + optimalPageCount
+ " pageBreaks.size()= " + algRestart.getPageBreaks().size());
boolean fitsOnePage
= optimalPageCount <= pslm.getCurrentPV()
.getBodyRegion().getMainReference().getCurrentSpan().getColumnCount();
if (needColumnBalancing) {
if (!fitsOnePage) {
AbstractBreaker.log.warn(
"Breaking algorithm produced more columns than are available.");
/* reenable when everything works
throw new IllegalStateException(
"Breaking algorithm must not produce more columns than available.");
*/
}
} else {
if (fitsOnePage) {
//Replace last page
pslm.setCurrentPage(pageProvider.getPage(false, currentPageNum));
} else {
//Last page-master cannot hold the content.
//Add areas now...
addAreas(alg, restartPoint, partCount - restartPoint, originalList, effectiveList);
//...and add a blank last page
setLastPageIndex(currentPageNum + 1);
pslm.setCurrentPage(pslm.makeNewPage(true));
return;
}
}
addAreas(algRestart, optimalPageCount, originalList, effectiveList);
}
private void setLastPageIndex(int currentPageNum) {
int lastPageIndex = pslm.getForcedLastPageNum(currentPageNum);
pageProvider.setLastPageIndex(lastPageIndex);
}
/** {@inheritDoc} */
protected void startPart(BlockSequence list, int breakClass) {
AbstractBreaker.log.debug("startPart() breakClass=" + getBreakClassName(breakClass));
if (pslm.getCurrentPage() == null) {
throw new IllegalStateException("curPage must not be null");
}
if (!pageBreakHandled) {
//firstPart is necessary because we need the first page before we start the
//algorithm so we have a BPD and IPD. This may subject to change later when we
//start handling more complex cases.
if (!firstPart) {
// if this is the first page that will be created by
// the current BlockSequence, it could have a break
// condition that must be satisfied;
// otherwise, we may simply need a new page
handleBreakTrait(breakClass);
}
pageProvider.setStartOfNextElementList(pslm.getCurrentPageNum(),
pslm.getCurrentPV().getCurrentSpan().getCurrentFlowIndex(),
this.spanAllActive);
}
pageBreakHandled = false;
// add static areas and resolve any new id areas
// finish page and add to area tree
firstPart = false;
}
/** {@inheritDoc} */
protected void handleEmptyContent() {
pslm.getCurrentPV().getPage().fakeNonEmpty();
}
/** {@inheritDoc} */
protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) {
// add footnote areas
if (!pslm.getTableHeaderFootnotes().isEmpty()
|| pbp.footnoteFirstListIndex < pbp.footnoteLastListIndex
|| pbp.footnoteFirstElementIndex <= pbp.footnoteLastElementIndex
|| !pslm.getTableFooterFootnotes().isEmpty()) {
for (List<KnuthElement> footnote : pslm.getTableHeaderFootnotes()) {
addFootnoteAreas(footnote);
}
// call addAreas() for each FootnoteBodyLM
for (int i = pbp.footnoteFirstListIndex; i <= pbp.footnoteLastListIndex; i++) {
List elementList = alg.getFootnoteList(i);
int firstIndex = (i == pbp.footnoteFirstListIndex
? pbp.footnoteFirstElementIndex : 0);
int lastIndex = (i == pbp.footnoteLastListIndex
? pbp.footnoteLastElementIndex : elementList.size() - 1);
addFootnoteAreas(elementList, firstIndex, lastIndex + 1);
}
for (List<KnuthElement> footnote : pslm.getTableFooterFootnotes()) {
addFootnoteAreas(footnote);
}
// set the offset from the top margin
Footnote parentArea = pslm.getCurrentPV().getBodyRegion().getFootnote();
int topOffset = pslm.getCurrentPV().getBodyRegion().getBPD() - parentArea.getBPD();
if (separatorArea != null) {
topOffset -= separatorArea.getBPD();
}
parentArea.setTop(topOffset);
parentArea.setSeparator(separatorArea);
}
pslm.getCurrentPV().getCurrentSpan().notifyFlowsFinished();
pslm.clearTableHeadingFootnotes();
}
private void addFootnoteAreas(List<KnuthElement> footnote) {
addFootnoteAreas(footnote, 0, footnote.size());
}
private void addFootnoteAreas(List<KnuthElement> footnote, int startIndex, int endIndex) {
SpaceResolver.performConditionalsNotification(footnote, startIndex, endIndex - 1, -1);
LayoutContext childLC = LayoutContext.newInstance();
AreaAdditionUtil.addAreas(null, new KnuthPossPosIter(footnote, startIndex, endIndex), childLC);
}
/** {@inheritDoc} */
protected FlowLayoutManager getCurrentChildLM() {
return childFLM;
}
/** {@inheritDoc} */
protected void observeElementList(List elementList) {
ElementListObserver.observe(elementList, "breaker",
pslm.getFObj().getId());
}
/**
* Depending on the kind of break condition, move to next column
* or page. May need to make an empty page if next page would
* not have the desired "handedness".
* @param breakVal - value of break-before or break-after trait.
*/
private void handleBreakTrait(int breakVal) {
Page curPage = pslm.getCurrentPage();
switch (breakVal) {
case Constants.EN_ALL:
//break due to span change in multi-column layout
curPage.getPageViewport().createSpan(true);
this.spanAllActive = true;
return;
case Constants.EN_NONE:
curPage.getPageViewport().createSpan(false);
this.spanAllActive = false;
return;
case Constants.EN_COLUMN:
case Constants.EN_AUTO:
case -1:
PageViewport pv = curPage.getPageViewport();
//Check if previous page was spanned
boolean forceNewPageWithSpan = false;
RegionBody rb = (RegionBody)curPage.getSimplePageMaster().getRegion(
Constants.FO_REGION_BODY);
forceNewPageWithSpan
= (rb.getColumnCount() > 1
&& pv.getCurrentSpan().getColumnCount() == 1);
if (forceNewPageWithSpan) {
log.trace("Forcing new page with span");
curPage = pslm.makeNewPage(false);
curPage.getPageViewport().createSpan(true);
} else if (pv.getCurrentSpan().hasMoreFlows()) {
log.trace("Moving to next flow");
pv.getCurrentSpan().moveToNextFlow();
} else {
log.trace("Making new page");
/*curPage = */pslm.makeNewPage(false);
}
return;
case Constants.EN_PAGE:
default:
log.debug("handling break-before after page " + pslm.getCurrentPageNum()
+ " breakVal=" + getBreakClassName(breakVal));
if (needBlankPageBeforeNew(breakVal)) {
log.trace("Inserting blank page");
/*curPage = */pslm.makeNewPage(true);
}
if (needNewPage(breakVal)) {
log.trace("Making new page");
/*curPage = */pslm.makeNewPage(false);
}
}
}
/**
* Check if a blank page is needed to accommodate
* desired even or odd page number.
* @param breakVal - value of break-before or break-after trait.
*/
private boolean needBlankPageBeforeNew(int breakVal) {
if (breakVal == Constants.EN_PAGE
|| (pslm.getCurrentPage().getPageViewport().getPage().isEmpty())) {
// any page is OK or we already have an empty page
return false;
} else {
/* IF we are on the kind of page we need, we'll need a new page. */
if (pslm.getCurrentPageNum() % 2 == 0) { // even page
return (breakVal == Constants.EN_EVEN_PAGE);
} else { // odd page
return (breakVal == Constants.EN_ODD_PAGE);
}
}
}
/**
* See if need to generate a new page
* @param breakVal - value of break-before or break-after trait.
*/
private boolean needNewPage(int breakVal) {
if (pslm.getCurrentPage().getPageViewport().getPage().isEmpty()) {
if (breakVal == Constants.EN_PAGE) {
return false;
} else if (pslm.getCurrentPageNum() % 2 == 0) { // even page
return (breakVal == Constants.EN_ODD_PAGE);
} else { // odd page
return (breakVal == Constants.EN_EVEN_PAGE);
}
} else {
return true;
}
}
protected boolean handlingStartOfFloat() {
return handlingStartOfFloat;
}
protected void handleStartOfFloat(int fHeight, int fYOffset) {
handlingStartOfFloat = true;
handlingEndOfFloat = false;
floatHeight = fHeight;
floatYOffset = fYOffset;
childFLM.handleFloatOn();
}
protected int getFloatHeight() {
return floatHeight;
}
protected int getFloatYOffset() {
return floatYOffset;
}
protected boolean handlingEndOfFloat() {
return handlingEndOfFloat;
}
protected void handleEndOfFloat(int fHeight) {
handlingEndOfFloat = true;
handlingStartOfFloat = false;
floatHeight = fHeight;
childFLM.handleFloatOff();
}
protected boolean handlingFloat() {
return (handlingStartOfFloat || handlingEndOfFloat);
}
public int getOffsetDueToFloat() {
handlingEndOfFloat = false;
return floatHeight + floatYOffset;
}
protected int handleFloatLayout(PageBreakingAlgorithm alg, int optimalPageCount, BlockSequence blockList,
LayoutContext childLC) {
pageBreakHandled = true;
List firstElements = Collections.EMPTY_LIST;
KnuthNode floatNode = alg.getBestFloatEdgeNode();
int floatPosition = floatNode.position;
KnuthElement floatElem = alg.getElement(floatPosition);
Position positionAtBreak = floatElem.getPosition();
if (!(positionAtBreak instanceof SpaceResolver.SpaceHandlingBreakPosition)) {
throw new UnsupportedOperationException("Don't know how to restart at position" + positionAtBreak);
}
/* Retrieve the original position wrapped into this space position */
positionAtBreak = positionAtBreak.getPosition();
addAreas(alg, optimalPageCount, blockList, blockList);
blockLists.clear();
blockListIndex = -1;
LayoutManager restartAtLM = null;
if (positionAtBreak != null && positionAtBreak.getIndex() == -1) {
if (positionAtBreak instanceof ListItemLayoutManager.ListItemPosition) {
restartAtLM = positionAtBreak.getLM();
} else {
Position position;
Iterator iter = blockList.listIterator(floatPosition + 1);
do {
KnuthElement nextElement = (KnuthElement) iter.next();
position = nextElement.getPosition();
} while (position == null || position instanceof SpaceResolver.SpaceHandlingPosition
|| position instanceof SpaceResolver.SpaceHandlingBreakPosition
&& position.getPosition().getIndex() == -1);
LayoutManager surroundingLM = positionAtBreak.getLM();
while (position.getLM() != surroundingLM) {
position = position.getPosition();
}
restartAtLM = position.getPosition().getLM();
}
}
int nextSequenceStartsOn = getNextBlockList(childLC, Constants.EN_COLUMN, positionAtBreak,
restartAtLM, firstElements);
return nextSequenceStartsOn;
}
protected void addAreasForFloats(PageBreakingAlgorithm alg, int startPart, int partCount,
BlockSequence originalList, BlockSequence effectiveList, final LayoutContext childLC,
int lastBreak, int startElementIndex, int endElementIndex) {
FloatPosition pbp = alg.getFloatPosition();
// Check the last break position for forced breaks
int lastBreakClass;
if (startElementIndex == 0) {
lastBreakClass = effectiveList.getStartOn();
} else {
ListElement lastBreakElement = effectiveList.getElement(endElementIndex);
if (lastBreakElement.isPenalty()) {
KnuthPenalty pen = (KnuthPenalty) lastBreakElement;
if (pen.getPenalty() == KnuthPenalty.INFINITE) {
/**
* That means that there was a keep.within-page="always", but that
* it's OK to break at a column. TODO The break class is being
* abused to implement keep.within-column and keep.within-page.
* This is very misleading and must be revised.
*/
lastBreakClass = Constants.EN_COLUMN;
} else {
lastBreakClass = pen.getBreakClass();
}
} else {
lastBreakClass = Constants.EN_COLUMN;
}
}
// the end of the new part
endElementIndex = pbp.getLeafPos();
// ignore the first elements added by the
// PageSequenceLayoutManager
startElementIndex += (startElementIndex == 0) ? effectiveList.ignoreAtStart : 0;
log.debug("PLM> part: " + (startPart + partCount + 1) + ", start at pos " + startElementIndex
+ ", break at pos " + endElementIndex + ", break class = "
+ getBreakClassName(lastBreakClass));
startPart(effectiveList, lastBreakClass);
int displayAlign = getCurrentDisplayAlign();
// The following is needed by SpaceResolver.performConditionalsNotification()
// further down as there may be important Position elements in the element list trailer
int notificationEndElementIndex = endElementIndex;
// ignore the last elements added by the
// PageSequenceLayoutManager
endElementIndex -= (endElementIndex == (originalList.size() - 1)) ? effectiveList.ignoreAtEnd : 0;
// ignore the last element in the page if it is a KnuthGlue
// object
if (((KnuthElement) effectiveList.get(endElementIndex)).isGlue()) {
endElementIndex--;
}
// ignore KnuthGlue and KnuthPenalty objects
// at the beginning of the line
startElementIndex = alg.par.getFirstBoxIndex(startElementIndex);
if (startElementIndex <= endElementIndex) {
if (log.isDebugEnabled()) {
log.debug(" addAreas from " + startElementIndex + " to " + endElementIndex);
}
// set the space adjustment ratio
childLC.setSpaceAdjust(pbp.bpdAdjust);
// add space before if display-align is center or bottom
// add space after if display-align is distribute and
// this is not the last page
if (pbp.difference != 0 && displayAlign == Constants.EN_CENTER) {
childLC.setSpaceBefore(pbp.difference / 2);
} else if (pbp.difference != 0 && displayAlign == Constants.EN_AFTER) {
childLC.setSpaceBefore(pbp.difference);
}
// Handle SpaceHandling(Break)Positions, see SpaceResolver!
SpaceResolver.performConditionalsNotification(effectiveList, startElementIndex,
notificationEndElementIndex, lastBreak);
// Add areas of lines, in the current page, before the float or during float
addAreas(new KnuthPossPosIter(effectiveList, startElementIndex, endElementIndex + 1), childLC);
// add areas for the float, if applicable
if (alg.handlingStartOfFloat()) {
for (int k = startElementIndex; k < endElementIndex + 1; k++) {
ListElement le = effectiveList.getElement(k);
if (le instanceof KnuthBlockBox) {
KnuthBlockBox kbb = (KnuthBlockBox) le;
for (FloatContentLayoutManager fclm : kbb.getFloatContentLMs()) {
fclm.processAreas(childLC);
int floatHeight = fclm.getFloatHeight();
int floatYOffset = fclm.getFloatYOffset();
PageSequenceLayoutManager pslm = (PageSequenceLayoutManager) getTopLevelLM();
pslm.recordStartOfFloat(floatHeight, floatYOffset);
}
}
}
}
if (alg.handlingEndOfFloat()) {
PageSequenceLayoutManager pslm = (PageSequenceLayoutManager) getTopLevelLM();
pslm.setEndIntrusionAdjustment(0);
pslm.setStartIntrusionAdjustment(0);
int effectiveFloatHeight = alg.getFloatHeight();
pslm.recordEndOfFloat(effectiveFloatHeight);
}
if (alg.handlingFloat()) {
PageSequenceLayoutManager pslm = (PageSequenceLayoutManager) getTopLevelLM();
alg.relayFootnotes(pslm);
}
} else {
// no content for this part
handleEmptyContent();
}
pageBreakHandled = true;
}
public void holdFootnotes(List fl, List ll, int tfl, int ifl, boolean fp, boolean nf, int fnfi, int fli,
int fei, MinOptMax fsl, int pfli, int pfei) {
relayedFootnotesList = fl;
relayedLengthList = ll;
relayedTotalFootnotesLength = tfl;
relayedInsertedFootnotesLength = ifl;
relayedFootnotesPending = fp;
relayedNewFootnotes = nf;
relayedFirstNewFootnoteIndex = fnfi;
relayedFootnoteListIndex = fli;
relayedFootnoteElementIndex = fei;
relayedFootnoteSeparatorLength = fsl;
previousFootnoteListIndex = pfli;
previousFootnoteElementIndex = pfei;
}
public void retrieveFootones(PageBreakingAlgorithm alg) {
if (relayedFootnotesList != null && relayedFootnotesList.size() > 0) {
alg.loadFootnotes(relayedFootnotesList, relayedLengthList, relayedTotalFootnotesLength,
relayedInsertedFootnotesLength, relayedFootnotesPending, relayedNewFootnotes,
relayedFirstNewFootnoteIndex, relayedFootnoteListIndex, relayedFootnoteElementIndex,
relayedFootnoteSeparatorLength, previousFootnoteListIndex,
previousFootnoteElementIndex);
relayedFootnotesList = null;
relayedLengthList = null;
relayedTotalFootnotesLength = 0;
relayedInsertedFootnotesLength = 0;
relayedFootnotesPending = false;
relayedNewFootnotes = false;
relayedFirstNewFootnoteIndex = 0;
relayedFootnoteListIndex = 0;
relayedFootnoteElementIndex = -1;
relayedFootnoteSeparatorLength = null;
}
}
}