123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619 |
- /*
- * $Id: InlineStackingLayoutManager.java,v 1.13 2003/03/05 20:38:26 jeremias Exp $
- * ============================================================================
- * The Apache Software License, Version 1.1
- * ============================================================================
- *
- * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modifica-
- * tion, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * 3. The end-user documentation included with the redistribution, if any, must
- * include the following acknowledgment: "This product includes software
- * developed by the Apache Software Foundation (http://www.apache.org/)."
- * Alternately, this acknowledgment may appear in the software itself, if
- * and wherever such third-party acknowledgments normally appear.
- *
- * 4. The names "FOP" and "Apache Software Foundation" must not be used to
- * endorse or promote products derived from this software without prior
- * written permission. For written permission, please contact
- * apache@apache.org.
- *
- * 5. Products derived from this software may not be called "Apache", nor may
- * "Apache" appear in their name, without prior written permission of the
- * Apache Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
- * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- * ============================================================================
- *
- * This software consists of voluntary contributions made by many individuals
- * on behalf of the Apache Software Foundation and was originally created by
- * James Tauber <jtauber@jtauber.com>. For more information on the Apache
- * Software Foundation, please see <http://www.apache.org/>.
- */
- package org.apache.fop.layoutmgr;
-
- import java.util.Iterator;
- import java.util.ListIterator;
- import java.util.HashMap;
-
- import org.apache.fop.fo.FObj;
- import org.apache.fop.fo.PropertyManager;
- import org.apache.fop.fo.properties.CommonBorderAndPadding;
- import org.apache.fop.fo.properties.CommonBackground;
- import org.apache.fop.traits.InlineProps;
- import org.apache.fop.area.Area;
- import org.apache.fop.area.inline.InlineArea;
- import org.apache.fop.area.inline.InlineParent;
- import org.apache.fop.area.inline.Space;
- import org.apache.fop.traits.MinOptMax;
-
- /**
- * LayoutManager for objects which stack children in the inline direction,
- * such as Inline or Line
- */
- public class InlineStackingLayoutManager extends AbstractLayoutManager {
-
-
- private static class StackingIter extends PositionIterator {
-
- StackingIter(Iterator parentIter) {
- super(parentIter);
- }
-
- protected LayoutProcessor getLM(Object nextObj) {
- return ((Position) nextObj).getPosition().getLM();
- }
-
- protected Position getPos(Object nextObj) {
- return ((Position) nextObj).getPosition();
- }
-
- }
-
-
- /**
- * Size of any start or end borders and padding.
- */
- private MinOptMax allocIPD = new MinOptMax(0);
-
- /**
- * Size of border and padding in BPD (ie, before and after).
- */
- private MinOptMax extraBPD;
-
-
- private InlineProps inlineProps = null;
- private CommonBorderAndPadding borderProps = null;
- private CommonBackground backgroundProps;
-
- private Area currentArea; // LineArea or InlineParent
-
- private BreakPoss prevBP;
- private LayoutContext childLC ;
-
- private LayoutManager lastChildLM = null; // Set when return last breakposs
- private boolean bAreaCreated = false;
-
- /** Used to store previous content IPD for each child LM. */
- private HashMap hmPrevIPD = new HashMap();
-
- /**
- * Create an inline stacking layout manager.
- * This is used for fo's that create areas that
- * contain inline areas.
- *
- * @param fobj the formatting object that creates the area
- * @param childLMiter the iterator for child areas
- */
- public InlineStackingLayoutManager() {
- }
-
- /**
- * Set the FO object for this layout manager
- *
- * @param fo the fo for this layout manager
- */
- public void setFObj(FObj fo) {
- this.fobj = fo;
- foID = fobj.getID();
- childLMiter = null;
- }
-
- /**
- * Set the iterator.
- *
- * @param iter the iterator for this LM
- */
- public void setLMiter(ListIterator iter) {
- childLMiter = iter;
- }
-
- /**
- * Check if this generates inline areas.
- * This creates inline areas that contain other inline areas.
- *
- * @return true
- */
- public boolean generatesInlineAreas() {
- return true;
- }
-
- /**
- * Initialize properties for this layout manager.
- *
- * @param propMgr the property manager from the fo that created this manager
- */
- protected void initProperties(PropertyManager propMgr) {
- // super.initProperties(propMgr);
- inlineProps = propMgr.getInlineProps();
- borderProps = propMgr.getBorderAndPadding();
- // Calculdate border and padding size in BPD
- int iPad = borderProps.getPadding(CommonBorderAndPadding.BEFORE, false);
- iPad += borderProps.getBorderWidth(CommonBorderAndPadding.BEFORE,
- false);
- iPad += borderProps.getPadding(CommonBorderAndPadding.AFTER, false);
- iPad += borderProps.getBorderWidth(CommonBorderAndPadding.AFTER, false);
- extraBPD = new MinOptMax(iPad);
-
- backgroundProps = propMgr.getBackgroundProps();
- }
-
- private MinOptMax getExtraIPD(boolean bNotFirst, boolean bNotLast) {
- int iBP = borderProps.getPadding(CommonBorderAndPadding.START,
- bNotFirst);
- iBP += borderProps.getBorderWidth(CommonBorderAndPadding.START,
- bNotFirst);
- iBP += borderProps.getPadding(CommonBorderAndPadding.END, bNotLast);
- iBP += borderProps.getBorderWidth(CommonBorderAndPadding.END, bNotLast);
- return new MinOptMax(iBP);
- }
-
-
- protected boolean hasLeadingFence(boolean bNotFirst) {
- int iBP = borderProps.getPadding(CommonBorderAndPadding.START,
- bNotFirst);
- iBP += borderProps.getBorderWidth(CommonBorderAndPadding.START,
- bNotFirst);
- return (iBP > 0);
- }
-
- protected boolean hasTrailingFence(boolean bNotLast) {
- int iBP = borderProps.getPadding(CommonBorderAndPadding.END, bNotLast);
- iBP += borderProps.getBorderWidth(CommonBorderAndPadding.END, bNotLast);
- return (iBP > 0);
- }
-
- /**
- * Reset position for returning next BreakPossibility.
- * @param prevPos a Position returned by this layout manager
- * representing a potential break decision.
- */
- public void resetPosition(Position prevPos) {
- if (prevPos != null) {
- // ASSERT (prevPos.getLM() == this)
- if (prevPos.getLM() != this) {
- //getLogger().error(
- // "InlineStackingLayoutManager.resetPosition: " +
- // "LM mismatch!!!");
- }
- // Back up the child LM Position
- Position childPos = prevPos.getPosition();
- reset(childPos);
- if (prevBP != null
- && prevBP.getLayoutManager() != childPos.getLM()) {
- childLC = null;
- }
- prevBP = new BreakPoss(childPos);
- } else {
- // Backup to start of first child layout manager
- prevBP = null;
- // super.resetPosition(prevPos);
- reset(prevPos);
- // If any areas created, we are restarting!
- bAreaCreated = false;
- }
- // Do we need to reset some context like pending or prevContent?
- // What about prevBP?
- }
-
- /**
- * Return value indicating whether the next area to be generated could
- * start a new line. This should only be called in the "START" condition
- * if a previous inline BP couldn't end the line.
- * Return true if any space-start, border-start or padding-start, else
- * propagate to first child LM
- */
- public boolean canBreakBefore(LayoutContext context) {
- if (inlineProps.spaceStart.getSpace().min > 0 || hasLeadingFence(false)) {
- return true;
- }
- LayoutProcessor lm = getChildLM();
- if (lm != null) {
- return lm.canBreakBefore(context);
- } else {
- return false; // ??? NO child LM?
- }
- }
-
- protected MinOptMax getPrevIPD(LayoutManager lm) {
- return (MinOptMax) hmPrevIPD.get(lm);
- }
-
- /**
- * Clear the previous IPD calculation.
- */
- protected void clearPrevIPD() {
- hmPrevIPD.clear();
- }
-
- /**
- * Get the next break position for this layout manager.
- * The next break position will be an position within the
- * areas return by the child inline layout managers.
- *
- * @param lc the layout context for finding breaks
- * @return the next break position
- */
- public BreakPoss getNextBreakPoss(LayoutContext lc) {
- // Get a break from currently active child LM
- BreakPoss bp = null;
- LayoutProcessor curLM;
- SpaceSpecifier leadingSpace = lc.getLeadingSpace();
-
- if (lc.startsNewArea()) {
- // First call to this LM in new parent "area", but this may
- // not be the first area created by this inline
- childLC = new LayoutContext(lc);
- lc.getLeadingSpace().addSpace(inlineProps.spaceStart);
-
- // Check for "fence"
- if (hasLeadingFence(!lc.isFirstArea())) {
- // Reset leading space sequence for child areas
- leadingSpace = new SpaceSpecifier(false);
- }
- // Reset state variables
- clearPrevIPD(); // Clear stored prev content dimensions
- }
-
- // We only do this loop more than once if a childLM returns
- // a null BreakPoss, meaning it has nothing (more) to layout.
- while ((curLM = getChildLM()) != null) {
-
- // ignore nested blocks for now
- if (!curLM.generatesInlineAreas()) {
- getLogger().warn("ignoring block inside inline fo");
- curLM.setFinished(true);
- continue;
- }
- /* If first break for this child LM, set START_AREA flag
- * and initialize pending space from previous LM sibling's
- * trailing space specifiers.
- */
- boolean bFirstChildBP = (prevBP == null
- || prevBP.getLayoutManager() != curLM);
-
- initChildLC(childLC, prevBP, lc.startsNewArea(),
- bFirstChildBP, leadingSpace);
- if (lc.tryHyphenate()) {
- childLC.setHyphContext(lc.getHyphContext());
- }
-
- if (((bp = curLM.getNextBreakPoss(childLC)) != null)
- || (lc.tryHyphenate()
- && !lc.getHyphContext().hasMoreHyphPoints())) {
- break;
- }
- // If LM has no content, should it generate any area? If not,
- // should trailing space from a previous area interact with
- // leading space from a following area?
- }
- if (bp == null) {
- setFinished(true);
- return null; // There was no childLM with anything to layout
- // Alternative is to return a BP with the isLast flag set
- } else {
- boolean bIsLast = false;
- if (getChildLM() == null) {
- bIsLast = true;
- setFinished(true);
- } else if (bp.couldEndLine()) {
- /* Child LM ends with suppressible spaces. See if it could
- * end this LM's area too. Child LM finish flag is NOT set!
- */
- bIsLast = !hasMoreLM(bp.getLayoutManager());
- }
- return makeBreakPoss(bp, lc, bIsLast);
- }
- }
-
- /** ATTENTION: ALSO USED BY LineLayoutManager! */
- protected void initChildLC(LayoutContext childLC, BreakPoss prevBP,
- boolean bStartParent, boolean bFirstChildBP,
- SpaceSpecifier leadingSpace) {
-
- childLC.setFlags(LayoutContext.NEW_AREA,
- (bFirstChildBP || bStartParent));
- if (bStartParent) {
- // Start of a new line area or inline parent area
- childLC.setFlags(LayoutContext.FIRST_AREA, bFirstChildBP);
- childLC.setLeadingSpace(leadingSpace);
- } else if (bFirstChildBP) {
- // Space-after sequence from previous "area"
- childLC.setFlags(LayoutContext.FIRST_AREA, true);
- childLC.setLeadingSpace(prevBP.getTrailingSpace());
- } else {
- childLC.setLeadingSpace(null);
- }
- }
-
-
- private BreakPoss makeBreakPoss(BreakPoss bp, LayoutContext lc,
- boolean bIsLast) {
- NonLeafPosition inlbp = new NonLeafPosition(this, bp.getPosition());
- BreakPoss myBP = new BreakPoss(inlbp, bp.getFlags());
-
- myBP.setFlag(BreakPoss.ISFIRST, lc.isFirstArea());
- myBP.setFlag(BreakPoss.ISLAST, bIsLast);
-
- if (bIsLast) {
- lastChildLM = bp.getLayoutManager();
- }
-
- // Update dimension information for our allocation area,
- // including child areas
- // generated by previous childLM which have completed layout
- // Update pending area measure
- // This includes all previous breakinfo
-
- MinOptMax bpDim = (MinOptMax) bp.getStackingSize().clone();
- MinOptMax prevIPD = updatePrevIPD(bp, prevBP, lc.startsNewArea(),
- lc.isFirstArea());
-
- if (lc.startsNewArea()) {
- myBP.setLeadingSpace(lc.getLeadingSpace());
- }
-
-
- // Add size of previous child areas which are finished
- bpDim.add(prevIPD);
-
- SpaceSpecifier trailingSpace = bp.getTrailingSpace();
- if (hasTrailingFence(!bIsLast)) {
- bpDim.add(bp.resolveTrailingSpace(false));
- trailingSpace = new SpaceSpecifier(false);
- } else {
- // Need this to avoid modifying pending space specifiers
- // on previous BP from child as we use these on the next
- // call in this LM
- trailingSpace = (SpaceSpecifier) trailingSpace.clone();
- }
- trailingSpace.addSpace(inlineProps.spaceEnd);
- myBP.setTrailingSpace(trailingSpace);
-
- // Add start and end borders and padding
- bpDim.add(getExtraIPD(!lc.isFirstArea(), !bIsLast));
- myBP.setStackingSize(bpDim);
- myBP.setNonStackingSize(
- MinOptMax.add(bp.getNonStackingSize(), extraBPD));
-
- prevBP = bp;
- // if (bIsLast) {
- // setFinished(true); // Our last area, so indicate done
- // }
- return myBP;
- }
-
-
- /** ATTENTION: ALSO USED BY LineLayoutManager! */
- protected MinOptMax updatePrevIPD(BreakPoss bp, BreakPoss prevBP,
- boolean bStartParent, boolean bFirstArea) {
- MinOptMax prevIPD = new MinOptMax(0);
-
- if (bStartParent) {
- if (hasLeadingFence(!bFirstArea)) {
- // Space-start before first child area placed
- prevIPD.add(bp.resolveLeadingSpace());
- }
- hmPrevIPD.put(bp.getLayoutManager(), prevIPD);
- } else {
- // In case of reset to a previous position, it may already
- // be calculated
- prevIPD = (MinOptMax) hmPrevIPD.get(bp.getLayoutManager());
- if (prevIPD == null) {
- // ASSERT(prevBP.getLayoutManager() != bp.getLayoutManager());
- /* This is first bp generated by child (in this parent area).
- * Calculate space-start on this area in combination with any
- * pending space-end with previous break.
- * Corresponds to Space between two child areas.
- */
- prevIPD = (MinOptMax) hmPrevIPD.get(
- prevBP.getLayoutManager());
- prevIPD = MinOptMax.add(prevIPD, bp.resolveLeadingSpace());
- prevIPD.add(prevBP.getStackingSize());
- hmPrevIPD.put(bp.getLayoutManager(), prevIPD);
- }
- }
- return prevIPD;
- }
-
- public void getWordChars(StringBuffer sbChars, Position bp1,
- Position bp2) {
- Position endPos = ((NonLeafPosition) bp2).getPosition();
- Position prevPos = null;
- if (bp1 != null) {
- prevPos = ((NonLeafPosition) bp1).getPosition();
- if (prevPos.getLM() != endPos.getLM()) {
- prevPos = null;
- }
- }
- endPos.getLM().getWordChars(sbChars, prevPos, endPos);
- }
-
- /******
- protected BreakableText getText(BreakPoss prevBP, BreakPoss lastBP) {
- }
- *****/
-
- protected InlineParent createArea() {
- return new InlineParent();
- }
-
- /**
- * Generate and add areas to parent area.
- * Set size of each area. This should only create and return one
- * inline area for any inline parent area.
- *
- * @param parentIter Iterator over Position information returned
- * by this LayoutManager.
- * @param dSpaceAdjust Factor controlling how much extra space to add
- * in order to justify the line.
- */
- public void addAreas(PositionIterator parentIter,
- LayoutContext context) {
- InlineParent parent = createArea();
- parent.setHeight(context.getLineHeight());
- parent.setOffset(0);
- setCurrentArea(parent);
-
- setChildContext(new LayoutContext(context)); // Store current value
-
- // If has fence, make a new leadingSS
- /* How to know if first area created by this LM? Keep a count and
- * reset it if getNextBreakPoss() is called again.
- */
- if (hasLeadingFence(bAreaCreated)) {
- getContext().setLeadingSpace(new SpaceSpecifier(false));
- getContext().setFlags(LayoutContext.RESOLVE_LEADING_SPACE,
- true);
- } else {
- getContext().setFlags(LayoutContext.RESOLVE_LEADING_SPACE,
- false);
- }
-
- context.getLeadingSpace().addSpace(inlineProps.spaceStart);
-
-
- // posIter iterates over positions returned by this LM
- StackingIter childPosIter = new StackingIter(parentIter);
- LayoutProcessor prevLM = null;
- LayoutProcessor childLM ;
- while ((childLM = childPosIter.getNextChildLM()) != null) {
- //getContext().setTrailingSpace(new SpaceSpecifier(false));
- childLM.addAreas(childPosIter, getContext());
- getContext().setLeadingSpace(getContext().getTrailingSpace());
- getContext().setFlags(LayoutContext.RESOLVE_LEADING_SPACE,
- true);
- prevLM = childLM;
- }
-
- /* If has trailing fence, resolve trailing space specs from descendants.
- * Otherwise, propagate any trailing space specs to parent LM via
- * the context object.
- * If the last child LM called return ISLAST in the context object
- * and it is the last child LM for this LM, then this must be
- * the last area for the current LM also.
- */
- boolean bIsLast =
- (getContext().isLastArea() && prevLM == lastChildLM);
- if (hasTrailingFence(bIsLast)) {
- addSpace(getCurrentArea(),
- getContext().getTrailingSpace().resolve(false),
- getContext().getSpaceAdjust());
- context.setTrailingSpace(new SpaceSpecifier(false));
- } else {
- // Propagate trailing space-spec sequence to parent LM in context
- context.setTrailingSpace(getContext().getTrailingSpace());
- }
- // Add own trailing space to parent context (or set on area?)
- if (context.getTrailingSpace() != null) {
- context.getTrailingSpace().addSpace(inlineProps.spaceEnd);
- }
-
- // Add border and padding to current area and set flags (FIRST, LAST ...)
- TraitSetter.setBorderPaddingTraits(getCurrentArea(),
- borderProps, bAreaCreated, !bIsLast);
-
- if (borderProps != null) {
- TraitSetter.addBorders(getCurrentArea(), borderProps);
- }
- if (backgroundProps != null) {
- TraitSetter.addBackground(getCurrentArea(), backgroundProps);
- }
-
- parentLM.addChild(getCurrentArea());
- context.setFlags(LayoutContext.LAST_AREA, bIsLast);
- bAreaCreated = true;
- }
-
- protected Area getCurrentArea() {
- return currentArea;
- }
-
- protected void setCurrentArea(Area area) {
- currentArea = area;
- }
-
- public void addChild(Area childArea) {
- // Make sure childArea is inline area
- if (childArea instanceof InlineArea) {
- Area parent = getCurrentArea();
- if (getContext().resolveLeadingSpace()) {
- addSpace(parent,
- getContext().getLeadingSpace().resolve(false),
- getContext().getSpaceAdjust());
- }
- parent.addChild(childArea);
- }
- }
-
- protected void setChildContext(LayoutContext lc) {
- childLC = lc;
- }
-
- // Current child layout context
- protected LayoutContext getContext() {
- return childLC ;
- }
-
- protected void addSpace(Area parentArea, MinOptMax spaceRange,
- double dSpaceAdjust) {
- if (spaceRange != null) {
- int iAdjust = spaceRange.opt;
- if (dSpaceAdjust > 0.0) {
- // Stretch by factor
- iAdjust += (int)((double)(spaceRange.max
- - spaceRange.opt) * dSpaceAdjust);
- } else if (dSpaceAdjust < 0.0) {
- // Shrink by factor
- iAdjust += (int)((double)(spaceRange.opt
- - spaceRange.min) * dSpaceAdjust);
- }
- if (iAdjust != 0) {
- //getLogger().debug("Add leading space: " + iAdjust);
- Space ls = new Space();
- ls.setWidth(iAdjust);
- parentArea.addChild(ls);
- }
- }
- }
-
- }
|