123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886 |
- /*
- * 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.Collections;
- import java.util.Iterator;
- import java.util.LinkedList;
- import java.util.List;
- import java.util.ListIterator;
-
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
-
- import org.apache.fop.events.EventBroadcaster;
- 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);
-
- private LayoutManager originalRestartAtLM;
- private Position positionAtBreak;
- private 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;
- LayoutManager restartAtLM = getRestartAtLM(alg, ipdChangesOnNextPage, onLastPageAndIPDChanges,
- visitedBefore, blockList, 1);
- if (restartAtLM == null || restartAtLM.getChildLMs().isEmpty()) {
- firstElementsForRestart = null;
- LayoutManager restartAtLM2 = getRestartAtLM(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;
- }
-
- private LayoutManager getRestartAtLM(PageBreakingAlgorithm alg, boolean ipdChangesOnNextPage,
- boolean onLastPageAndIPDChanges, boolean visitedBefore,
- BlockSequence blockList, int start) {
- KnuthNode optimalBreak = ipdChangesOnNextPage ? alg.getBestNodeBeforeIPDChange() : alg
- .getBestNodeForLastPage();
- if (onLastPageAndIPDChanges && visitedBefore && this.originalRestartAtLM == null) {
- optimalBreak = null;
- }
- int positionIndex = findPositionIndex(optimalBreak, alg, start);
- if (ipdChangesOnNextPage || (positionAtBreak != null && positionAtBreak.getIndex() > -1)) {
- firstElementsForRestart = Collections.EMPTY_LIST;
- if (ipdChangesOnNextPage) {
- if (containsNonRestartableLM(positionAtBreak)) {
- if (alg.getIPDdifference() > 0) {
- EventBroadcaster eventBroadcaster = getCurrentChildLM().getFObj()
- .getUserAgent().getEventBroadcaster();
- BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider
- .get(eventBroadcaster);
- eventProducer.nonRestartableContentFlowingToNarrowerPage(this);
- }
- firstElementsForRestart = new LinkedList();
- boolean boxFound = false;
- Iterator iter = blockList.listIterator(positionIndex + 1);
- Position position = null;
- while (iter.hasNext()
- && (position == null || containsNonRestartableLM(position))) {
- positionIndex++;
- KnuthElement element = (KnuthElement) iter.next();
- position = element.getPosition();
- if (element.isBox()) {
- boxFound = true;
- firstElementsForRestart.add(element);
- } else if (boxFound) {
- firstElementsForRestart.add(element);
- }
- }
- if (position instanceof SpaceResolver.SpaceHandlingBreakPosition) {
- /* Retrieve the original position wrapped into this space position */
- positionAtBreak = position.getPosition();
- } else {
- positionAtBreak = null;
- }
- }
- }
- }
- LayoutManager restartAtLM = null;
- if (ipdChangesOnNextPage || !(positionAtBreak != null && positionAtBreak.getIndex() > -1)) {
- if (positionAtBreak != null && positionAtBreak.getIndex() == -1) {
- Position position;
- Iterator iter = blockList.listIterator(positionIndex + 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();
- }
- if (onLastPageAndIPDChanges && restartAtLM != null) {
- if (originalRestartAtLM == null) {
- originalRestartAtLM = restartAtLM;
- } else {
- restartAtLM = originalRestartAtLM;
- }
- firstElementsForRestart = Collections.EMPTY_LIST;
- }
- }
- if (onLastPageAndIPDChanges && !visitedBefore && positionAtBreak.getPosition() != null) {
- restartAtLM = positionAtBreak.getPosition().getLM();
- }
- return restartAtLM;
- }
-
- private int findPositionIndex(KnuthNode optimalBreak, PageBreakingAlgorithm alg, int start) {
- int positionIndex = (optimalBreak != null) ? optimalBreak.position : start;
- for (int i = positionIndex; i < alg.par.size(); i++) {
- KnuthElement elementAtBreak = alg.getElement(i);
- if (elementAtBreak.getPosition() == null) {
- elementAtBreak = alg.getElement(0);
- }
- positionAtBreak = elementAtBreak.getPosition();
- /* Retrieve the original position wrapped into this space position */
- positionAtBreak = positionAtBreak.getPosition();
- if (positionAtBreak != null) {
- return i;
- }
- }
- return positionIndex;
- }
-
- /**
- * 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
- */
- private 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();
- }
- }
|