| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* $Id$ */ |
| |
| package org.apache.fop.layoutmgr.inline; |
| |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.ListIterator; |
| |
| import org.apache.fop.area.Area; |
| import org.apache.fop.area.inline.Space; |
| import org.apache.fop.fo.FObj; |
| import org.apache.fop.fo.properties.SpaceProperty; |
| import org.apache.fop.layoutmgr.AbstractLayoutManager; |
| import org.apache.fop.layoutmgr.BreakOpportunity; |
| import org.apache.fop.layoutmgr.BreakOpportunityHelper; |
| import org.apache.fop.layoutmgr.KnuthElement; |
| import org.apache.fop.layoutmgr.LayoutContext; |
| import org.apache.fop.layoutmgr.NonLeafPosition; |
| import org.apache.fop.layoutmgr.Position; |
| import org.apache.fop.traits.MinOptMax; |
| |
| /** |
| * Class modelling the commonalities of layoutmanagers for objects |
| * which stack children in the inline direction, such as Inline or |
| * Line. It should not be instantiated directly. |
| */ |
| public abstract class InlineStackingLayoutManager extends AbstractLayoutManager implements |
| InlineLevelLayoutManager, BreakOpportunity { |
| |
| /** |
| * Size of border and padding in BPD (ie, before and after). |
| */ |
| protected MinOptMax extraBPD; |
| |
| private Area currentArea; // LineArea or InlineParent |
| |
| /** The child layout context */ |
| protected LayoutContext childLC; |
| |
| /** |
| * Create an inline stacking layout manager. |
| * This is used for fo's that create areas that |
| * contain inline areas. |
| * |
| * @param node the formatting object that creates the area |
| */ |
| protected InlineStackingLayoutManager(FObj node) { |
| super(node); |
| extraBPD = MinOptMax.ZERO; // @SuppressFBWarnings("URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD") |
| } |
| |
| /** |
| * Set the iterator. |
| * |
| * @param iter the iterator for this LM |
| */ |
| public void setLMiter(ListIterator iter) { |
| childLMiter = iter; |
| } |
| |
| /** |
| * Returns the extra IPD needed for any leading or trailing fences for the |
| * current area. |
| * @param bNotFirst true if not the first area for this layout manager |
| * @param bNotLast true if not the last area for this layout manager |
| * @return the extra IPD as a MinOptMax spec |
| */ |
| protected MinOptMax getExtraIPD(boolean bNotFirst, boolean bNotLast) { |
| return MinOptMax.ZERO; |
| } |
| |
| |
| /** |
| * Indication if the current area has a leading fence. |
| * @param bNotFirst true if not the first area for this layout manager |
| * @return the leading fence flag |
| */ |
| protected boolean hasLeadingFence(boolean bNotFirst) { |
| return false; |
| } |
| |
| /** |
| * Indication if the current area has a trailing fence. |
| * @param bNotLast true if not the last area for this layout manager |
| * @return the trailing fence flag |
| */ |
| protected boolean hasTrailingFence(boolean bNotLast) { |
| return false; |
| } |
| |
| /** |
| * Get the space at the start of the inline area. |
| * @return the space property describing the space |
| */ |
| protected SpaceProperty getSpaceStart() { |
| return null; |
| } |
| |
| /** |
| * Get the space at the end of the inline area. |
| * @return the space property describing the space |
| */ |
| protected SpaceProperty getSpaceEnd() { |
| return null; |
| } |
| |
| /** |
| * Returns the current area. |
| * @return the current area |
| */ |
| protected Area getCurrentArea() { |
| return currentArea; |
| } |
| |
| /** |
| * Set the current area. |
| * @param area the current area |
| */ |
| protected void setCurrentArea(Area area) { |
| currentArea = area; |
| } |
| |
| /** |
| * Trait setter to be overridden by subclasses. |
| * @param bNotFirst true if this is not the first child area added |
| * @param bNotLast true if this is not the last child area added |
| */ |
| protected void setTraits(boolean bNotFirst, boolean bNotLast) { |
| } |
| |
| /** |
| * Set the current child layout context |
| * @param lc the child layout context |
| */ |
| protected void setChildContext(LayoutContext lc) { |
| childLC = lc; |
| } |
| |
| /** |
| * Current child layout context |
| * @return the current child layout context |
| */ |
| protected LayoutContext getContext() { |
| return childLC; |
| } |
| |
| /** |
| * Adds a space to the area. |
| * |
| * @param parentArea the area to which to add the space |
| * @param spaceRange the space range specifier |
| * @param spaceAdjust the factor by which to stretch or shrink the space |
| */ |
| protected void addSpace(Area parentArea, MinOptMax spaceRange, double spaceAdjust) { |
| if (spaceRange != null) { |
| int iAdjust = spaceRange.getOpt(); |
| if (spaceAdjust > 0.0) { |
| // Stretch by factor |
| iAdjust += (int) (spaceRange.getStretch() * spaceAdjust); |
| } else if (spaceAdjust < 0.0) { |
| // Shrink by factor |
| iAdjust += (int) (spaceRange.getShrink() * spaceAdjust); |
| } |
| if (iAdjust != 0) { |
| //getLogger().debug("Add leading space: " + iAdjust); |
| Space ls = new Space(); |
| ls.setIPD(iAdjust); |
| int level = parentArea.getBidiLevel(); |
| if (level >= 0) { |
| ls.setBidiLevel(level); |
| } |
| parentArea.addChildArea(ls); |
| } |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| public List addALetterSpaceTo(List oldList) { |
| return addALetterSpaceTo(oldList, 0); |
| } |
| |
| /** {@inheritDoc} */ |
| public List addALetterSpaceTo(List oldList, int thisDepth) { |
| // old list contains only a box, or the sequence: box penalty glue box |
| |
| ListIterator oldListIterator = oldList.listIterator(oldList.size()); |
| KnuthElement element = (KnuthElement) oldListIterator.previous(); |
| int depth = thisDepth + 1; |
| |
| // The last element may not have a layout manager (its position == null); |
| // this may happen if it is a padding box; see bug 39571. |
| Position pos = element.getPosition(); |
| InlineLevelLayoutManager lm = null; |
| if (pos != null) { |
| lm = (InlineLevelLayoutManager) pos.getLM(depth); |
| } |
| if (lm == null) { |
| return oldList; |
| } |
| oldList = lm.addALetterSpaceTo(oldList, depth); |
| // "wrap" the Position stored in new elements of oldList |
| oldListIterator = oldList.listIterator(); |
| while (oldListIterator.hasNext()) { |
| element = (KnuthElement) oldListIterator.next(); |
| pos = element.getPosition(); |
| lm = null; |
| if (pos != null) { |
| lm = (InlineLevelLayoutManager) pos.getLM(thisDepth); |
| } |
| // in old elements the position at thisDepth is a position for this LM |
| // only wrap new elements |
| if (lm != this) { |
| // new element, wrap position |
| element.setPosition(notifyPos(new NonLeafPosition(this, element.getPosition()))); |
| } |
| } |
| |
| return oldList; |
| } |
| |
| /** {@inheritDoc} */ |
| public String getWordChars(Position pos) { |
| Position newPos = pos.getPosition(); |
| return ((InlineLevelLayoutManager) newPos.getLM()).getWordChars(newPos); |
| } |
| |
| /** {@inheritDoc} */ |
| public void hyphenate(Position pos, HyphContext hc) { |
| Position newPos = pos.getPosition(); |
| ((InlineLevelLayoutManager) |
| newPos.getLM()).hyphenate(newPos, hc); |
| } |
| |
| /** {@inheritDoc} */ |
| public boolean applyChanges(List oldList) { |
| return applyChanges(oldList, 0); |
| } |
| |
| /** {@inheritDoc} */ |
| public boolean applyChanges(List oldList, int depth) { |
| ListIterator oldListIterator = oldList.listIterator(); |
| KnuthElement oldElement; |
| depth += 1; |
| |
| InlineLevelLayoutManager prevLM = null; |
| InlineLevelLayoutManager currLM; |
| int fromIndex = 0; |
| |
| boolean bSomethingChanged = false; |
| while (oldListIterator.hasNext()) { |
| oldElement = (KnuthElement) oldListIterator.next(); |
| Position pos = oldElement.getPosition(); |
| if (pos == null) { |
| currLM = null; |
| } else { |
| currLM = (InlineLevelLayoutManager) pos.getLM(depth); |
| } |
| |
| // initialize prevLM |
| if (prevLM == null) { |
| prevLM = currLM; |
| } |
| |
| if (currLM != prevLM || !oldListIterator.hasNext()) { |
| if (prevLM == this || currLM == this) { |
| prevLM = currLM; |
| } else if (oldListIterator.hasNext()) { |
| bSomethingChanged |
| = prevLM.applyChanges(oldList.subList(fromIndex, |
| oldListIterator.previousIndex()), |
| depth) |
| || bSomethingChanged; |
| prevLM = currLM; |
| fromIndex = oldListIterator.previousIndex(); |
| } else if (currLM == prevLM) { |
| bSomethingChanged |
| = (prevLM != null) |
| && prevLM.applyChanges(oldList.subList(fromIndex, |
| oldList.size()), depth) |
| || bSomethingChanged; |
| } else { |
| bSomethingChanged |
| = prevLM.applyChanges(oldList.subList(fromIndex, |
| oldListIterator.previousIndex()), |
| depth) |
| || bSomethingChanged; |
| if (currLM != null) { |
| bSomethingChanged |
| = currLM.applyChanges(oldList.subList(oldListIterator.previousIndex(), |
| oldList.size()), depth) |
| || bSomethingChanged; |
| } |
| } |
| } |
| } |
| |
| return bSomethingChanged; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public List getChangedKnuthElements(List oldList, int alignment) { |
| return getChangedKnuthElements(oldList, alignment, 0); |
| } |
| |
| /** {@inheritDoc} */ |
| public List getChangedKnuthElements(List oldList, int alignment, int depth) { |
| // "unwrap" the Positions stored in the elements |
| ListIterator oldListIterator = oldList.listIterator(); |
| KnuthElement oldElement; |
| depth += 1; |
| |
| KnuthElement returnedElement; |
| LinkedList returnedList = new LinkedList(); |
| LinkedList returnList = new LinkedList(); |
| InlineLevelLayoutManager prevLM = null; |
| InlineLevelLayoutManager currLM; |
| int fromIndex = 0; |
| |
| while (oldListIterator.hasNext()) { |
| oldElement = (KnuthElement) oldListIterator.next(); |
| Position pos = oldElement.getPosition(); |
| if (pos == null) { |
| currLM = null; |
| } else { |
| currLM = (InlineLevelLayoutManager) pos.getLM(depth); |
| } |
| if (prevLM == null) { |
| prevLM = currLM; |
| } |
| |
| if (currLM != prevLM || !oldListIterator.hasNext()) { |
| if (oldListIterator.hasNext()) { |
| returnedList.addAll( |
| prevLM.getChangedKnuthElements( |
| oldList.subList(fromIndex, oldListIterator.previousIndex()), |
| alignment, depth)); |
| prevLM = currLM; |
| fromIndex = oldListIterator.previousIndex(); |
| } else if (currLM == prevLM) { |
| returnedList.addAll( |
| prevLM.getChangedKnuthElements( |
| oldList.subList(fromIndex, oldList.size()), |
| alignment, depth)); |
| } else { |
| returnedList.addAll( |
| prevLM.getChangedKnuthElements( |
| oldList.subList(fromIndex, oldListIterator.previousIndex()), |
| alignment, depth)); |
| if (currLM != null) { |
| returnedList.addAll( |
| currLM.getChangedKnuthElements( |
| oldList.subList(oldListIterator.previousIndex(), oldList.size()), |
| alignment, depth)); |
| } |
| } |
| } |
| } |
| |
| // this is a new list |
| // "wrap" the Position stored in each element of returnedList |
| ListIterator listIter = returnedList.listIterator(); |
| while (listIter.hasNext()) { |
| returnedElement = (KnuthElement) listIter.next(); |
| returnedElement.setPosition( |
| notifyPos(new NonLeafPosition(this, returnedElement.getPosition()))); |
| returnList.add(returnedElement); |
| } |
| |
| return returnList; |
| } |
| |
| public int getBreakBefore() { |
| return BreakOpportunityHelper.getBreakBefore(this); |
| } |
| |
| } |