123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- /*
- * 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 java.util.List;
- import java.util.ListIterator;
- import java.util.Stack;
-
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
-
- import org.apache.fop.area.Area;
- import org.apache.fop.area.BlockParent;
- import org.apache.fop.fo.pagination.Flow;
- import org.apache.fop.util.ListUtil;
-
- /**
- * LayoutManager for an fo:flow object.
- * Its parent LM is the PageSequenceLayoutManager.
- * This LM is responsible for getting columns of the appropriate size
- * and filling them with block-level areas generated by its children.
- * TODO Reintroduce emergency counter (generate error to avoid endless loop)
- */
- public class FlowLayoutManager extends BlockStackingLayoutManager
- implements BlockLevelLayoutManager {
-
- /**
- * logging instance
- */
- private static Log log = LogFactory.getLog(FlowLayoutManager.class);
-
- /** Array of areas currently being filled stored by area class */
- private BlockParent[] currentAreas = new BlockParent[Area.CLASS_MAX];
-
- /**
- * This is the top level layout manager.
- * It is created by the PageSequence FO.
- * @param pslm parent PageSequenceLayoutManager object
- * @param node Flow object
- */
- public FlowLayoutManager(PageSequenceLayoutManager pslm, Flow node) {
- super(node);
- setGeneratesBlockArea(true);
- setParent(pslm);
- }
-
- /** {@inheritDoc} */
- @Override
- public List getNextKnuthElements(LayoutContext context, int alignment) {
- return getNextKnuthElements(context, alignment, null, 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
- * @param restartPosition {@link Position} to restart from
- * @param restartLM {@link LayoutManager} to restart from
- * @return the list of KnuthElements
- * @see LayoutManager#getNextKnuthElements(LayoutContext,int)
- */
- List getNextKnuthElements(LayoutContext context, int alignment,
- Position restartPosition, LayoutManager restartLM) {
-
- List<ListElement> elements = new LinkedList<ListElement>();
-
- boolean isRestart = (restartPosition != null);
- // always reset in case of restart (exception: see below)
- boolean doReset = isRestart;
- LayoutManager currentChildLM;
- Stack<LayoutManager> lmStack = new Stack<LayoutManager>();
- if (isRestart) {
- currentChildLM = restartPosition.getLM();
- if (currentChildLM == null) {
- throw new IllegalStateException("Cannot find layout manager to restart from");
- }
- if (restartLM != null && restartLM.getParent() == this) {
- currentChildLM = restartLM;
- } else {
- while (currentChildLM.getParent() != this) {
- lmStack.push(currentChildLM);
- currentChildLM = currentChildLM.getParent();
- }
- doReset = false;
- }
- setCurrentChildLM(currentChildLM);
- } else {
- currentChildLM = getChildLM();
- }
-
- while (currentChildLM != null) {
- if (!isRestart || doReset) {
- if (doReset) {
- currentChildLM.reset(); // TODO won't work with forced breaks
- }
- if (addChildElements(elements, currentChildLM, context, alignment,
- null, null, null) != null) {
- return elements;
- }
- } else {
- if (addChildElements(elements, currentChildLM, context, alignment, lmStack,
- restartPosition, restartLM) != null) {
- return elements;
- }
- // restarted; force reset as of next child
- doReset = true;
- }
- currentChildLM = getChildLM();
- }
-
- SpaceResolver.resolveElementList(elements);
- setFinished(true);
-
- assert !elements.isEmpty();
- return elements;
- }
-
- private List<ListElement> addChildElements(List<ListElement> elements,
- LayoutManager childLM, LayoutContext context, int alignment,
- Stack<LayoutManager> lmStack, Position position, LayoutManager restartAtLM) {
- if (handleSpanChange(childLM, context)) {
- SpaceResolver.resolveElementList(elements);
- return elements;
- }
-
- LayoutContext childLC = makeChildLayoutContext(context);
- List<ListElement> childElements
- = getNextChildElements(childLM, context, childLC, alignment, lmStack,
- position, restartAtLM);
- if (elements.isEmpty()) {
- context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
- }
- if (!elements.isEmpty()
- && !ElementListUtils.startsWithForcedBreak(childElements)) {
- addInBetweenBreak(elements, context, childLC);
- }
- context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
-
- elements.addAll(childElements);
-
- if (ElementListUtils.endsWithForcedBreak(elements)) {
- // a descendant of this flow has break-before or break-after
- if (childLM.isFinished() && !hasNextChildLM()) {
- setFinished(true);
- }
- SpaceResolver.resolveElementList(elements);
- return elements;
- }
- return null;
- }
-
- private boolean handleSpanChange(LayoutManager childLM, LayoutContext context) {
- int span = EN_NONE;
- int disableColumnBalancing = EN_FALSE;
- if (childLM instanceof BlockLayoutManager) {
- span = ((BlockLayoutManager)childLM).getBlockFO().getSpan();
- disableColumnBalancing = ((BlockLayoutManager) childLM).getBlockFO()
- .getDisableColumnBalancing();
- } else if (childLM instanceof BlockContainerLayoutManager) {
- span = ((BlockContainerLayoutManager)childLM).getBlockContainerFO().getSpan();
- disableColumnBalancing = ((BlockContainerLayoutManager) childLM).getBlockContainerFO()
- .getDisableColumnBalancing();
- }
-
- int currentSpan = context.getCurrentSpan();
- if (currentSpan != span) {
- if (span == EN_ALL) {
- context.setDisableColumnBalancing(disableColumnBalancing);
- }
- log.debug("span change from " + currentSpan + " to " + span);
- context.signalSpanChange(span);
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Overridden to take into account the current page-master's
- * writing-mode
- * {@inheritDoc}
- */
- @Override
- protected LayoutContext makeChildLayoutContext(LayoutContext context) {
- LayoutContext childLC = LayoutContext.newInstance();
- childLC.setStackLimitBP(context.getStackLimitBP());
- childLC.setRefIPD(context.getRefIPD());
- childLC.setWritingMode(getCurrentPage().getSimplePageMaster().getWritingMode());
- return childLC;
- }
-
- /**
- * Overridden to wrap the child positions before returning the list
- * {@inheritDoc}
- */
- @Override
- protected List<ListElement> getNextChildElements(LayoutManager childLM, LayoutContext context,
- LayoutContext childLC, int alignment, Stack<LayoutManager> lmStack,
- Position restartPosition, LayoutManager restartLM) {
-
- List<ListElement> childElements;
- if (lmStack == null) {
- childElements = childLM.getNextKnuthElements(childLC, alignment);
- } else {
- childElements = childLM.getNextKnuthElements(childLC, alignment,
- lmStack, restartPosition, restartLM);
- }
- assert !childElements.isEmpty();
-
- // "wrap" the Position inside each element
- List tempList = childElements;
- childElements = new LinkedList<ListElement>();
- wrapPositionElements(tempList, childElements);
- return childElements;
- }
-
- /** {@inheritDoc} */
- @Override
- public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
- log.debug(" FLM.negotiateBPDAdjustment> " + adj);
-
- if (lastElement.getPosition() instanceof NonLeafPosition) {
- // this element was not created by this FlowLM
- NonLeafPosition savedPos = (NonLeafPosition)lastElement.getPosition();
- lastElement.setPosition(savedPos.getPosition());
- int returnValue = ((BlockLevelLayoutManager)lastElement.getLayoutManager())
- .negotiateBPDAdjustment(adj, lastElement);
- lastElement.setPosition(savedPos);
- log.debug(" FLM.negotiateBPDAdjustment> result " + returnValue);
- return returnValue;
- } else {
- return 0;
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public void discardSpace(KnuthGlue spaceGlue) {
- log.debug(" FLM.discardSpace> ");
-
- if (spaceGlue.getPosition() instanceof NonLeafPosition) {
- // this element was not created by this FlowLM
- NonLeafPosition savedPos = (NonLeafPosition)spaceGlue.getPosition();
- spaceGlue.setPosition(savedPos.getPosition());
- ((BlockLevelLayoutManager) spaceGlue.getLayoutManager()).discardSpace(spaceGlue);
- spaceGlue.setPosition(savedPos);
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public Keep getKeepTogether() {
- return Keep.KEEP_AUTO;
- }
-
- /** {@inheritDoc} */
- @Override
- public Keep getKeepWithNext() {
- return Keep.KEEP_AUTO;
- }
-
- /** {@inheritDoc} */
- @Override
- public Keep getKeepWithPrevious() {
- return Keep.KEEP_AUTO;
- }
-
- /** {@inheritDoc} */
- @Override
- public List<KnuthElement> getChangedKnuthElements(List oldList, int alignment) {
- ListIterator<KnuthElement> oldListIterator = oldList.listIterator();
- KnuthElement returnedElement;
- List<KnuthElement> returnedList = new LinkedList<KnuthElement>();
- List<KnuthElement> returnList = new LinkedList<KnuthElement>();
- KnuthElement prevElement = null;
- KnuthElement currElement = null;
- int fromIndex = 0;
-
- // "unwrap" the Positions stored in the elements
- KnuthElement oldElement;
- while (oldListIterator.hasNext()) {
- oldElement = oldListIterator.next();
- if (oldElement.getPosition() instanceof NonLeafPosition) {
- // oldElement was created by a descendant of this FlowLM
- oldElement.setPosition((oldElement.getPosition()).getPosition());
- } else {
- // thisElement was created by this FlowLM, remove it
- oldListIterator.remove();
- }
- }
- // reset the iterator
- oldListIterator = oldList.listIterator();
-
-
- while (oldListIterator.hasNext()) {
- currElement = oldListIterator.next();
- if (prevElement != null
- && prevElement.getLayoutManager() != currElement.getLayoutManager()) {
- // prevElement is the last element generated by the same LM
- BlockLevelLayoutManager prevLM = (BlockLevelLayoutManager)
- prevElement.getLayoutManager();
- BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
- currElement.getLayoutManager();
- returnedList.addAll(prevLM.getChangedKnuthElements(
- oldList.subList(fromIndex, oldListIterator.previousIndex()), alignment));
- fromIndex = oldListIterator.previousIndex();
-
- // there is another block after this one
- if (prevLM.mustKeepWithNext()
- || currLM.mustKeepWithPrevious()) {
- // add an infinite penalty to forbid a break between blocks
- returnedList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
- new Position(this), false));
- } else if (!ListUtil.getLast(returnedList).isGlue()) {
- // add a null penalty to allow a break between blocks
- returnedList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
- }
- }
- prevElement = currElement;
- }
- if (currElement != null) {
- BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
- currElement.getLayoutManager();
- returnedList.addAll(currLM.getChangedKnuthElements(
- oldList.subList(fromIndex, oldList.size()), alignment));
- }
-
- // "wrap" the Position stored in each element of returnedList
- // and add elements to returnList
- ListIterator<KnuthElement> listIter = returnedList.listIterator();
- while (listIter.hasNext()) {
- returnedElement = listIter.next();
- if (returnedElement.getLayoutManager() != this) {
- returnedElement.setPosition(
- new NonLeafPosition(this, returnedElement.getPosition()));
- }
- returnList.add(returnedElement);
- }
-
- return returnList;
- }
-
- /** {@inheritDoc} */
- @Override
- public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
- AreaAdditionUtil.addAreas(this, parentIter, layoutContext);
- flush();
- }
-
- /**
- * Add child area to a the correct container, depending on its
- * area class. A Flow can fill at most one area container of any class
- * at any one time. The actual work is done by BlockStackingLM.
- *
- * @param childArea the area to add
- */
- @Override
- public void addChildArea(Area childArea) {
- getParentArea(childArea);
- addChildToArea(childArea,
- this.currentAreas[childArea.getAreaClass()]);
- }
-
- /** {@inheritDoc} */
- @Override
- public Area getParentArea(Area childArea) {
- BlockParent parentArea = null;
- int aclass = childArea.getAreaClass();
-
- if (aclass == Area.CLASS_NORMAL) {
- parentArea = getCurrentPV().getCurrentFlow();
- } else if (aclass == Area.CLASS_BEFORE_FLOAT) {
- parentArea = getCurrentPV().getBodyRegion().getBeforeFloat();
- } else if (aclass == Area.CLASS_FOOTNOTE) {
- parentArea = getCurrentPV().getBodyRegion().getFootnote();
- } else {
- throw new IllegalStateException("(internal error) Invalid "
- + "area class (" + aclass + ") requested.");
- }
-
- this.currentAreas[aclass] = parentArea;
- setCurrentArea(parentArea);
- return parentArea;
- }
-
- /**
- * Returns the IPD of the content area
- * @return the IPD of the content area
- */
- @Override
- public int getContentAreaIPD() {
- return getCurrentPV().getCurrentSpan().getColumnWidth();
- }
-
- /**
- * Returns the BPD of the content area
- * @return the BPD of the content area
- */
- @Override
- public int getContentAreaBPD() {
- return getCurrentPV().getBodyRegion().getBPD();
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean isRestartable() {
- return true;
- }
-
- }
|