blob: 625b520ed42189f3b90a3d0bccd5dc6e47c4b114 [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.List;
import java.util.ListIterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.fo.Constants;
import org.apache.fop.layoutmgr.BreakingAlgorithm.KnuthNode;
import org.apache.fop.traits.MinOptMax;
import org.apache.fop.util.ListUtil;
/**
* Abstract base class for breakers (page breakers, static region handlers etc.).
*/
public abstract class AbstractBreaker {
/** logging instance */
protected static final Log log = LogFactory.getLog(AbstractBreaker.class);
protected LayoutManager originalRestartAtLM;
protected Position positionAtBreak;
protected List firstElementsForRestart;
protected PageSequenceLayoutManager pslm;
/**
* A page break position.
*/
public static class PageBreakPosition extends LeafPosition {
// Percentage to adjust (stretch or shrink)
double bpdAdjust;
int difference;
int footnoteFirstListIndex;
int footnoteFirstElementIndex;
int footnoteLastListIndex;
int footnoteLastElementIndex;
PageBreakPosition(LayoutManager lm, int breakIndex,
int ffli, int ffei, int flli, int flei,
double bpdA, int diff) {
super(lm, breakIndex);
bpdAdjust = bpdA;
difference = diff;
footnoteFirstListIndex = ffli;
footnoteFirstElementIndex = ffei;
footnoteLastListIndex = flli;
footnoteLastElementIndex = flei;
}
}
public static class FloatPosition extends LeafPosition {
double bpdAdjust; // Percentage to adjust (stretch or shrink)
int difference;
FloatPosition(LayoutManager lm, int breakIndex, double bpdA, int diff) {
super(lm, breakIndex);
bpdAdjust = bpdA;
difference = diff;
}
}
/**
* Helper method, mainly used to improve debug/trace output
* @param breakClassId the {@link Constants} enum value.
* @return the break class name
*/
static String getBreakClassName(int breakClassId) {
switch (breakClassId) {
case Constants.EN_ALL: return "ALL";
case Constants.EN_ANY: return "ANY";
case Constants.EN_AUTO: return "AUTO";
case Constants.EN_COLUMN: return "COLUMN";
case Constants.EN_EVEN_PAGE: return "EVEN PAGE";
case Constants.EN_LINE: return "LINE";
case Constants.EN_NONE: return "NONE";
case Constants.EN_ODD_PAGE: return "ODD PAGE";
case Constants.EN_PAGE: return "PAGE";
default: return "??? (" + String.valueOf(breakClassId) + ")";
}
}
/**
* Helper class, extending the functionality of the
* basic {@link BlockKnuthSequence}.
*/
public static class BlockSequence extends BlockKnuthSequence {
private static final long serialVersionUID = -5348831120146774118L;
/** Number of elements to ignore at the beginning of the list. */
int ignoreAtStart;
/** Number of elements to ignore at the end of the list. */
int ignoreAtEnd;
/**
* startOn represents where on the page/which page layout
* should start for this BlockSequence. Acceptable values:
* Constants.EN_ANY (can continue from finished location
* of previous BlockSequence?), EN_COLUMN, EN_ODD_PAGE,
* EN_EVEN_PAGE.
*/
private final int startOn;
private final int displayAlign;
/**
* Creates a new BlockSequence.
* @param startOn the kind of page the sequence should start on.
* One of {@link Constants#EN_ANY}, {@link Constants#EN_COLUMN},
* {@link Constants#EN_ODD_PAGE}, or {@link Constants#EN_EVEN_PAGE}.
* @param displayAlign the value for the display-align property
*/
public BlockSequence(int startOn, int displayAlign) {
super();
this.startOn = startOn;
this.displayAlign = displayAlign;
}
/**
* @return the kind of page the sequence should start on.
* One of {@link Constants#EN_ANY}, {@link Constants#EN_COLUMN},
* {@link Constants#EN_ODD_PAGE}, or {@link Constants#EN_EVEN_PAGE}.
*/
public int getStartOn() {
return this.startOn;
}
/** @return the value for the display-align property */
public int getDisplayAlign() {
return this.displayAlign;
}
/**
* Finalizes a Knuth sequence.
* @return a finalized sequence.
*/
@Override
public KnuthSequence endSequence() {
return endSequence(null);
}
/**
* Finalizes a Knuth sequence.
* @param breakPosition a Position instance for the last penalty (may be null)
* @return a finalized sequence.
*/
public KnuthSequence endSequence(Position breakPosition) {
// remove glue and penalty item at the end of the paragraph
while (this.size() > ignoreAtStart
&& !((KnuthElement) ListUtil.getLast(this)).isBox()) {
ListUtil.removeLast(this);
}
if (this.size() > ignoreAtStart) {
// add the elements representing the space at the end of the last line
// and the forced break
this.add(new KnuthPenalty(0, KnuthElement.INFINITE,
false, null, false));
this.add(new KnuthGlue(0, 10000000, 0, null, false));
this.add(new KnuthPenalty(0, -KnuthElement.INFINITE,
false, breakPosition, false));
ignoreAtEnd = 3;
return this;
} else {
this.clear();
return null;
}
}
/**
* Finalizes a this {@link BlockSequence}, adding a terminating
* penalty-glue-penalty sequence
* @param breakPosition a Position instance pointing to the last penalty
* @return the finalized {@link BlockSequence}
*/
public BlockSequence endBlockSequence(Position breakPosition) {
KnuthSequence temp = endSequence(breakPosition);
if (temp != null) {
BlockSequence returnSequence = new BlockSequence(startOn, displayAlign);
returnSequence.addAll(temp);
returnSequence.ignoreAtEnd = this.ignoreAtEnd;
return returnSequence;
} else {
return null;
}
}
}
// used by doLayout and getNextBlockList*
protected List<BlockSequence> blockLists;
private boolean empty = true;
/** blockListIndex of the current BlockSequence in blockLists */
protected int blockListIndex;
/** desired text alignment */
protected int alignment;
private int alignmentLast;
/** footnote separator length */
protected MinOptMax footnoteSeparatorLength = MinOptMax.ZERO;
/** @return current display alignment */
protected abstract int getCurrentDisplayAlign();
/** @return true if content not exhausted */
protected abstract boolean hasMoreContent();
/**
* Tell the layout manager to add all the child areas implied
* by Position objects which will be returned by the
* Iterator.
*
* @param posIter the position iterator
* @param context the context
*/
protected abstract void addAreas(PositionIterator posIter, LayoutContext context);
/** @return top level layout manager */
protected abstract LayoutManager getTopLevelLM();
/** @return current child layout manager */
protected abstract LayoutManager getCurrentChildLM();
/**
* Controls the behaviour of the algorithm in cases where the first element of a part
* overflows a line/page.
* @return true if the algorithm should try to send the element to the next line/page.
*/
protected boolean isPartOverflowRecoveryActivated() {
return true;
}
/**
* @return true if one a single part should be produced if possible (ex. for block-containers)
*/
protected boolean isSinglePartFavored() {
return false;
}
/**
* Returns the PageProvider if any. PageBreaker overrides this method because each
* page may have a different available BPD which needs to be accessible to the breaking
* algorithm.
* @return the applicable PageProvider, or null if not applicable
*/
protected PageProvider getPageProvider() {
return null;
}
/**
* Creates and returns a PageBreakingLayoutListener for the PageBreakingAlgorithm to
* notify about layout problems.
* @return the listener instance or null if no notifications are needed
*/
protected PageBreakingAlgorithm.PageBreakingLayoutListener createLayoutListener() {
return null;
}
/**
* Get a sequence of KnuthElements representing the content
* of the node assigned to the LM
*
* @param context the LayoutContext used to store layout information
* @param alignment the desired text alignment
* @return the list of KnuthElements
*/
protected abstract List<KnuthElement> getNextKnuthElements(LayoutContext context,
int alignment);
/**
* Get a sequence of KnuthElements representing the content
* of the node assigned to the LM
*
* @param context the LayoutContext used to store layout information
* @param alignment the desired text alignment
* @param positionAtIPDChange last element on the part before an IPD change
* @param restartAtLM the layout manager from which to restart, if IPD
* change occurs between two LMs
* @return the list of KnuthElements
*/
protected List<KnuthElement> getNextKnuthElements(LayoutContext context, int alignment,
Position positionAtIPDChange, LayoutManager restartAtLM) {
throw new UnsupportedOperationException("TODO: implement acceptable fallback");
}
/** @return true if there's no content that could be handled. */
public boolean isEmpty() {
return empty;
}
/**
* Start part.
* @param list a block sequence
* @param breakClass a break class
*/
protected void startPart(BlockSequence list, int breakClass, boolean emptyContent) {
//nop
}
/**
* This method is called when no content is available for a part. Used to force empty pages.
*/
protected void handleEmptyContent() {
//nop
}
/**
* Finish part.
* @param alg a page breaking algorithm
* @param pbp a page break posittion
*/
protected abstract void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp);
/**
* Creates the top-level LayoutContext for the breaker operation.
* @return the top-level LayoutContext
*/
protected LayoutContext createLayoutContext() {
return LayoutContext.newInstance();
}
/**
* Used to update the LayoutContext in subclasses prior to starting a new element list.
* @param context the LayoutContext to update
*/
protected void updateLayoutContext(LayoutContext context) {
//nop
}
/**
* Used for debugging purposes. Notifies all registered observers about the element list.
* Override to set different parameters.
* @param elementList the Knuth element list
*/
protected void observeElementList(List elementList) {
ElementListObserver.observe(elementList, "breaker", null);
}
/**
* Starts the page breaking process.
* @param flowBPD the constant available block-progression-dimension (used for every part)
* @param autoHeight true if warnings about overflows should be disabled because the
* the BPD is really undefined (for footnote-separators, for example)
*/
public boolean doLayout(int flowBPD, boolean autoHeight) {
LayoutContext childLC = createLayoutContext();
childLC.setStackLimitBP(MinOptMax.getInstance(flowBPD));
alignment = Constants.EN_START;
alignmentLast = Constants.EN_START;
childLC.setBPAlignment(alignment);
BlockSequence blockList;
blockLists = new java.util.ArrayList<BlockSequence>();
log.debug("PLM> flow BPD =" + flowBPD);
int nextSequenceStartsOn = Constants.EN_ANY;
while (hasMoreContent()) {
blockLists.clear();
//*** Phase 1: Get Knuth elements ***
nextSequenceStartsOn = getNextBlockList(childLC, nextSequenceStartsOn);
empty = empty && blockLists.size() == 0;
//*** Phases 2 and 3 ***
log.debug("PLM> blockLists.size() = " + blockLists.size());
for (blockListIndex = 0; blockListIndex < blockLists.size(); blockListIndex++) {
blockList = blockLists.get(blockListIndex);
//debug code start
if (log.isDebugEnabled()) {
log.debug(" blockListIndex = " + blockListIndex);
log.debug(" sequence starts on " + getBreakClassName(blockList.startOn));
}
observeElementList(blockList);
//debug code end
//*** Phase 2: Alignment and breaking ***
log.debug("PLM> start of algorithm (" + this.getClass().getName()
+ "), flow BPD =" + flowBPD);
PageBreakingAlgorithm alg = new PageBreakingAlgorithm(getTopLevelLM(),
getPageProvider(), createLayoutListener(),
alignment, alignmentLast, footnoteSeparatorLength,
isPartOverflowRecoveryActivated(), autoHeight, isSinglePartFavored());
alg.setConstantLineWidth(flowBPD);
int optimalPageCount = alg.findBreakingPoints(blockList, 1, true,
BreakingAlgorithm.ALL_BREAKS);
boolean ipdChangesOnNextPage = (alg.getIPDdifference() != 0);
boolean onLastPageAndIPDChanges = false;
if (!ipdChangesOnNextPage) {
onLastPageAndIPDChanges = (lastPageHasIPDChange() && !thereIsANonRestartableLM(alg)
&& (shouldRedoLayout() || (wasLayoutRedone() && optimalPageCount > 1)));
}
if ((ipdChangesOnNextPage || hasMoreContent() || optimalPageCount > 1)
&& pslm != null && pslm.getCurrentPage().isPagePositionOnly) {
return false;
}
if (alg.handlingFloat()) {
nextSequenceStartsOn = handleFloatLayout(alg, optimalPageCount, blockList, childLC);
} else if (ipdChangesOnNextPage || onLastPageAndIPDChanges) {
boolean visitedBefore = false;
if (onLastPageAndIPDChanges) {
visitedBefore = wasLayoutRedone();
prepareToRedoLayout(alg, optimalPageCount, blockList, blockList);
}
firstElementsForRestart = null;
RestartAtLM restartAtLMClass = new RestartAtLM();
LayoutManager restartAtLM = restartAtLMClass.getRestartAtLM(this, alg, ipdChangesOnNextPage,
onLastPageAndIPDChanges, visitedBefore, blockList, 1);
if (restartAtLMClass.invalidPosition) {
return false;
}
if (restartAtLM == null || restartAtLM.getChildLMs().isEmpty()) {
firstElementsForRestart = null;
LayoutManager restartAtLM2 = new RestartAtLM().getRestartAtLM(this, alg, ipdChangesOnNextPage,
onLastPageAndIPDChanges, visitedBefore, blockList, 0);
if (restartAtLM2 != null) {
restartAtLM = restartAtLM2;
}
}
if (ipdChangesOnNextPage) {
addAreas(alg, optimalPageCount, blockList, blockList);
}
blockLists.clear();
blockListIndex = -1;
nextSequenceStartsOn = getNextBlockList(childLC, Constants.EN_COLUMN, positionAtBreak,
restartAtLM, firstElementsForRestart);
} else {
log.debug("PLM> optimalPageCount= " + optimalPageCount
+ " pageBreaks.size()= " + alg.getPageBreaks().size());
//*** Phase 3: Add areas ***
doPhase3(alg, optimalPageCount, blockList, blockList);
}
}
}
// done
blockLists = null;
return true;
}
/**
* Returns {@code true} if the given position or one of its descendants
* corresponds to a non-restartable LM.
*
* @param position a position
* @return {@code true} if there is a non-restartable LM in the hierarchy
*/
protected boolean containsNonRestartableLM(Position position) {
LayoutManager lm = position.getLM();
if (lm != null && !lm.isRestartable()) {
return true;
} else {
Position subPosition = position.getPosition();
return subPosition != null && containsNonRestartableLM(subPosition);
}
}
/**
* Phase 3 of Knuth algorithm: Adds the areas
* @param alg PageBreakingAlgorithm instance which determined the breaks
* @param partCount number of parts (pages) to be rendered
* @param originalList original Knuth element list
* @param effectiveList effective Knuth element list (after adjustments)
*/
protected abstract void doPhase3(PageBreakingAlgorithm alg, int partCount,
BlockSequence originalList, BlockSequence effectiveList);
/**
* Phase 3 of Knuth algorithm: Adds the areas
* @param alg PageBreakingAlgorithm instance which determined the breaks
* @param partCount number of parts (pages) to be rendered
* @param originalList original Knuth element list
* @param effectiveList effective Knuth element list (after adjustments)
*/
protected void addAreas(PageBreakingAlgorithm alg, int partCount,
BlockSequence originalList, BlockSequence effectiveList) {
addAreas(alg, 0, partCount, originalList, effectiveList);
}
protected void addAreas(PageBreakingAlgorithm alg, int startPart, int partCount,
BlockSequence originalList, BlockSequence effectiveList) {
addAreas(alg, startPart, partCount, originalList, effectiveList, LayoutContext.newInstance());
}
/**
* Phase 3 of Knuth algorithm: Adds the areas
* @param alg PageBreakingAlgorithm instance which determined the breaks
* @param startPart index of the first part (page) to be rendered
* @param partCount number of parts (pages) to be rendered
* @param originalList original Knuth element list
* @param effectiveList effective Knuth element list (after adjustments)
*/
protected void addAreas(PageBreakingAlgorithm alg, int startPart, int partCount,
BlockSequence originalList, BlockSequence effectiveList, final LayoutContext childLC) {
int startElementIndex = 0;
int endElementIndex = 0;
int lastBreak = -1;
for (int p = startPart; p < startPart + partCount; p++) {
PageBreakPosition pbp = alg.getPageBreaks().get(p);
// Check the last break position for forced breaks
int lastBreakClass;
if (p == 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: " + (p + 1)
+ ", start at pos " + startElementIndex
+ ", break at pos " + endElementIndex
+ ", break class = " + getBreakClassName(lastBreakClass));
startPart(effectiveList, lastBreakClass, startElementIndex > endElementIndex);
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 now!
addAreas(new KnuthPossPosIter(effectiveList, startElementIndex, endElementIndex + 1), childLC);
} else {
// no content for this part
handleEmptyContent();
}
finishPart(alg, pbp);
lastBreak = endElementIndex;
startElementIndex = pbp.getLeafPos() + 1;
}
if (alg.handlingFloat()) {
addAreasForFloats(alg, startPart, partCount, originalList, effectiveList, childLC, lastBreak,
startElementIndex, endElementIndex);
}
}
/**
* Notifies the layout managers about the space and conditional length situation based on
* the break decisions.
* @param effectiveList Element list to be painted
* @param startElementIndex start index of the part
* @param endElementIndex end index of the part
* @param lastBreak index of the last break element
*/
/**
* Handles span changes reported through the <code>LayoutContext</code>.
* Only used by the PSLM and called by <code>getNextBlockList()</code>.
* @param childLC the LayoutContext
* @param nextSequenceStartsOn previous value for break handling
* @return effective value for break handling
*/
protected int handleSpanChange(LayoutContext childLC, int nextSequenceStartsOn) {
return nextSequenceStartsOn;
}
/**
* Gets the next block list (sequence) and adds it to a list of block lists if it's not empty.
* @param childLC LayoutContext to use
* @param nextSequenceStartsOn indicates on what page the next sequence should start
* @return the page on which the next content should appear after a hard break
*/
protected int getNextBlockList(LayoutContext childLC, int nextSequenceStartsOn) {
return getNextBlockList(childLC, nextSequenceStartsOn, null, null, null);
}
/**
* Gets the next block list (sequence) and adds it to a list of block lists
* if it's not empty.
*
* @param childLC LayoutContext to use
* @param nextSequenceStartsOn indicates on what page the next sequence
* should start
* @param positionAtIPDChange last element on the part before an IPD change
* @param restartAtLM the layout manager from which to restart, if IPD
* change occurs between two LMs
* @param firstElements elements from non-restartable LMs on the new page
* @return the page on which the next content should appear after a hard
* break
*/
protected int getNextBlockList(LayoutContext childLC, int nextSequenceStartsOn,
Position positionAtIPDChange, LayoutManager restartAtLM,
List<KnuthElement> firstElements) {
updateLayoutContext(childLC);
//Make sure the span change signal is reset
childLC.signalSpanChange(Constants.NOT_SET);
BlockSequence blockList;
List<KnuthElement> returnedList;
if (firstElements == null) {
returnedList = getNextKnuthElements(childLC, alignment);
} else if (positionAtIPDChange == null) {
/*
* No restartable element found after changing IPD break. Simply add the
* non-restartable elements found after the break.
*/
returnedList = firstElements;
/*
* Remove the last 3 penalty-filler-forced break elements that were added by
* the Knuth algorithm. They will be re-added later on.
*/
if (returnedList.size() > 2) {
ListIterator iter = returnedList.listIterator(returnedList.size());
for (int i = 0; i < 3; i++) {
iter.previous();
iter.remove();
}
}
} else {
returnedList = getNextKnuthElements(childLC, alignment, positionAtIPDChange,
restartAtLM);
returnedList.addAll(0, firstElements);
}
if (returnedList != null) {
if (returnedList.isEmpty()) {
nextSequenceStartsOn = handleSpanChange(childLC, nextSequenceStartsOn);
return nextSequenceStartsOn;
}
blockList = new BlockSequence(nextSequenceStartsOn, getCurrentDisplayAlign());
//Only implemented by the PSLM
nextSequenceStartsOn = handleSpanChange(childLC, nextSequenceStartsOn);
Position breakPosition = null;
if (ElementListUtils.endsWithForcedBreak(returnedList)) {
KnuthPenalty breakPenalty = (KnuthPenalty) ListUtil
.removeLast(returnedList);
breakPosition = breakPenalty.getPosition();
log.debug("PLM> break - " + getBreakClassName(breakPenalty.getBreakClass()));
switch (breakPenalty.getBreakClass()) {
case Constants.EN_PAGE:
nextSequenceStartsOn = Constants.EN_ANY;
break;
case Constants.EN_COLUMN:
//TODO Fix this when implementing multi-column layout
nextSequenceStartsOn = Constants.EN_COLUMN;
break;
case Constants.EN_ODD_PAGE:
nextSequenceStartsOn = Constants.EN_ODD_PAGE;
break;
case Constants.EN_EVEN_PAGE:
nextSequenceStartsOn = Constants.EN_EVEN_PAGE;
break;
default:
throw new IllegalStateException("Invalid break class: "
+ breakPenalty.getBreakClass());
}
if (ElementListUtils.isEmptyBox(returnedList)) {
ListUtil.removeLast(returnedList);
}
}
blockList.addAll(returnedList);
BlockSequence seq;
seq = blockList.endBlockSequence(breakPosition);
if (seq != null) {
blockLists.add(seq);
}
}
return nextSequenceStartsOn;
}
protected boolean shouldRedoLayout() {
return false;
}
protected void prepareToRedoLayout(PageBreakingAlgorithm alg, int partCount,
BlockSequence originalList, BlockSequence effectiveList) {
return;
}
protected boolean wasLayoutRedone() {
return false;
}
private boolean thereIsANonRestartableLM(PageBreakingAlgorithm alg) {
KnuthNode optimalBreak = alg.getBestNodeForLastPage();
if (optimalBreak != null) {
int positionIndex = optimalBreak.position;
KnuthElement elementAtBreak = alg.getElement(positionIndex);
Position positionAtBreak = elementAtBreak.getPosition();
if (!(positionAtBreak instanceof SpaceResolver.SpaceHandlingBreakPosition)) {
return false;
}
/* Retrieve the original position wrapped into this space position */
positionAtBreak = positionAtBreak.getPosition();
if (positionAtBreak != null && containsNonRestartableLM(positionAtBreak)) {
return true;
}
}
return false;
}
protected boolean lastPageHasIPDChange() {
return false;
}
protected int handleFloatLayout(PageBreakingAlgorithm alg, int optimalPageCount, BlockSequence blockList,
LayoutContext childLC) {
throw new IllegalStateException();
}
protected void addAreasForFloats(PageBreakingAlgorithm alg, int startPart, int partCount,
BlockSequence originalList, BlockSequence effectiveList, final LayoutContext childLC,
int lastBreak, int startElementIndex, int endElementIndex) {
throw new IllegalStateException();
}
}