0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: /* $Id: BlockStackingLayoutManager.java 478928 2006-11-24 17:32:48Z vhennebert $ */
0019:
0020: package org.apache.fop.layoutmgr;
0021:
0022: import java.util.Iterator;
0023: import java.util.LinkedList;
0024: import java.util.List;
0025: import java.util.ListIterator;
0026:
0027: import org.apache.commons.logging.Log;
0028: import org.apache.commons.logging.LogFactory;
0029: import org.apache.fop.area.Area;
0030: import org.apache.fop.area.BlockParent;
0031: import org.apache.fop.area.Block;
0032: import org.apache.fop.fo.FObj;
0033: import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
0034: import org.apache.fop.fo.properties.SpaceProperty;
0035: import org.apache.fop.layoutmgr.inline.InlineLayoutManager;
0036: import org.apache.fop.layoutmgr.inline.LineLayoutManager;
0037: import org.apache.fop.traits.MinOptMax;
0038:
0039: /**
0040: * Base LayoutManager class for all areas which stack their child
0041: * areas in the block-progression direction, such as Flow, Block, ListBlock.
0042: */
0043: public abstract class BlockStackingLayoutManager extends
0044: AbstractLayoutManager implements BlockLevelLayoutManager {
0045:
0046: /**
0047: * logging instance
0048: */
0049: private static Log log = LogFactory
0050: .getLog(BlockStackingLayoutManager.class);
0051:
0052: /**
0053: * Reference to FO whose areas it's managing or to the traits
0054: * of the FO.
0055: */
0056: //protected LayoutManager curChildLM = null; AbstractLayoutManager also defines this!
0057: protected BlockParent parentArea = null;
0058:
0059: /** Value of the block-progression-unit (non-standard property) */
0060: protected int bpUnit = 0;
0061: /** space-before value adjusted for block-progression-unit handling */
0062: protected int adjustedSpaceBefore = 0;
0063: /** space-after value adjusted for block-progression-unit handling */
0064: protected int adjustedSpaceAfter = 0;
0065: /** Only used to store the original list when createUnitElements is called */
0066: protected LinkedList storedList = null;
0067: /** Indicates whether break before has been served or not */
0068: protected boolean breakBeforeServed = false;
0069: /** Indicates whether the first visible mark has been returned by this LM, yet */
0070: protected boolean firstVisibleMarkServed = false;
0071: /** Reference IPD available */
0072: protected int referenceIPD = 0;
0073: /** the effective start-indent value */
0074: protected int startIndent = 0;
0075: /** the effective end-indent value */
0076: protected int endIndent = 0;
0077: /**
0078: * Holds the (one-time use) fo:block space-before
0079: * and -after properties. Large fo:blocks are split
0080: * into multiple Area. Blocks to accomodate the subsequent
0081: * regions (pages) they are placed on. space-before
0082: * is applied at the beginning of the first
0083: * Block and space-after at the end of the last Block
0084: * used in rendering the fo:block.
0085: */
0086: protected MinOptMax foSpaceBefore = null;
0087: /** see foSpaceBefore */
0088: protected MinOptMax foSpaceAfter = null;
0089:
0090: private Position auxiliaryPosition;
0091:
0092: private int contentAreaIPD = 0;
0093:
0094: /**
0095: * @param node the fo this LM deals with
0096: */
0097: public BlockStackingLayoutManager(FObj node) {
0098: super (node);
0099: setGeneratesBlockArea(true);
0100: }
0101:
0102: /**
0103: * @return current area being filled
0104: */
0105: protected BlockParent getCurrentArea() {
0106: return this .parentArea;
0107: }
0108:
0109: /**
0110: * Set the current area being filled.
0111: * @param parentArea the current area to be filled
0112: */
0113: protected void setCurrentArea(BlockParent parentArea) {
0114: this .parentArea = parentArea;
0115: }
0116:
0117: /**
0118: * Add a block spacer for space before and space after a block.
0119: * This adds an empty Block area that acts as a block space.
0120: *
0121: * @param adjust the adjustment value
0122: * @param minoptmax the min/opt/max value of the spacing
0123: */
0124: public void addBlockSpacing(double adjust, MinOptMax minoptmax) {
0125: int sp = TraitSetter.getEffectiveSpace(adjust, minoptmax);
0126: if (sp != 0) {
0127: Block spacer = new Block();
0128: spacer.setBPD(sp);
0129: parentLM.addChildArea(spacer);
0130: }
0131: }
0132:
0133: /**
0134: * Add the childArea to the passed area.
0135: * Called by child LayoutManager when it has filled one of its areas.
0136: * The LM should already have an Area in which to put the child.
0137: * See if the area will fit in the current area.
0138: * If so, add it. Otherwise initiate breaking.
0139: * @param childArea the area to add: will be some block-stacked Area.
0140: * @param parentArea the area in which to add the childArea
0141: */
0142: protected void addChildToArea(Area childArea, BlockParent parentArea) {
0143: // This should be a block-level Area (Block in the generic sense)
0144: if (!(childArea instanceof Block)) {
0145: //log.error("Child not a Block in BlockStackingLM!");
0146: }
0147:
0148: parentArea.addBlock((Block) childArea);
0149: flush(); // hand off current area to parent
0150: }
0151:
0152: /**
0153: * Add the childArea to the current area.
0154: * Called by child LayoutManager when it has filled one of its areas.
0155: * The LM should already have an Area in which to put the child.
0156: * See if the area will fit in the current area.
0157: * If so, add it. Otherwise initiate breaking.
0158: * @param childArea the area to add: will be some block-stacked Area.
0159: */
0160: public void addChildArea(Area childArea) {
0161: addChildToArea(childArea, getCurrentArea());
0162: }
0163:
0164: /**
0165: * Force current area to be added to parent area.
0166: */
0167: protected void flush() {
0168: if (getCurrentArea() != null) {
0169: parentLM.addChildArea(getCurrentArea());
0170: }
0171: }
0172:
0173: /** @return a cached auxiliary Position instance used for things like spaces. */
0174: protected Position getAuxiliaryPosition() {
0175: if (this .auxiliaryPosition == null) {
0176: this .auxiliaryPosition = new NonLeafPosition(this , null);
0177: }
0178: return this .auxiliaryPosition;
0179: }
0180:
0181: /**
0182: * @param len length in millipoints to span with bp units
0183: * @return the minimum integer n such that n * bpUnit >= len
0184: */
0185: protected int neededUnits(int len) {
0186: return (int) Math.ceil((float) len / bpUnit);
0187: }
0188:
0189: /**
0190: * Determines and sets the content area IPD based on available reference area IPD, start- and
0191: * end-indent properties.
0192: * end-indent is adjusted based on overconstrained geometry rules, if necessary.
0193: * @return the resulting content area IPD
0194: */
0195: protected int updateContentAreaIPDwithOverconstrainedAdjust() {
0196: int ipd = referenceIPD - (startIndent + endIndent);
0197: if (ipd < 0) {
0198: //5.3.4, XSL 1.0, Overconstrained Geometry
0199: log
0200: .debug("Adjusting end-indent based on overconstrained geometry rules for "
0201: + fobj);
0202: endIndent += ipd;
0203: ipd = 0;
0204: //TODO Should we skip layout for a block that has ipd=0?
0205: }
0206: setContentAreaIPD(ipd);
0207: return ipd;
0208: }
0209:
0210: /**
0211: * Sets the content area IPD by directly supplying the value.
0212: * end-indent is adjusted based on overconstrained geometry rules, if necessary.
0213: * @return the resulting content area IPD
0214: */
0215: protected int updateContentAreaIPDwithOverconstrainedAdjust(
0216: int contentIPD) {
0217: int ipd = referenceIPD
0218: - (contentIPD + (startIndent + endIndent));
0219: if (ipd < 0) {
0220: //5.3.4, XSL 1.0, Overconstrained Geometry
0221: log
0222: .debug("Adjusting end-indent based on overconstrained geometry rules for "
0223: + fobj);
0224: endIndent += ipd;
0225: }
0226: setContentAreaIPD(contentIPD);
0227: return contentIPD;
0228: }
0229:
0230: /**
0231: * @see LayoutManager#getNextKnuthElements(LayoutContext, int)
0232: */
0233: public LinkedList getNextKnuthElements(LayoutContext context,
0234: int alignment) {
0235: //log.debug("BLM.getNextKnuthElements> keep-together = "
0236: // + layoutProps.keepTogether.getType());
0237: //log.debug(" keep-with-previous = " +
0238: // layoutProps.keepWithPrevious.getType());
0239: //log.debug(" keep-with-next = " +
0240: // layoutProps.keepWithNext.getType());
0241: BlockLevelLayoutManager curLM; // currently active LM
0242: BlockLevelLayoutManager prevLM = null; // previously active LM
0243:
0244: referenceIPD = context.getRefIPD();
0245:
0246: updateContentAreaIPDwithOverconstrainedAdjust();
0247:
0248: LinkedList returnedList = null;
0249: LinkedList contentList = new LinkedList();
0250: LinkedList returnList = new LinkedList();
0251:
0252: if (!breakBeforeServed) {
0253: try {
0254: if (addKnuthElementsForBreakBefore(returnList, context)) {
0255: return returnList;
0256: }
0257: } finally {
0258: breakBeforeServed = true;
0259: }
0260: }
0261:
0262: if (!firstVisibleMarkServed) {
0263: addKnuthElementsForSpaceBefore(returnList, alignment);
0264: }
0265:
0266: addKnuthElementsForBorderPaddingBefore(returnList,
0267: !firstVisibleMarkServed);
0268: firstVisibleMarkServed = true;
0269:
0270: //Spaces, border and padding to be repeated at each break
0271: addPendingMarks(context);
0272:
0273: while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) {
0274: LayoutContext childLC = new LayoutContext(0);
0275: childLC.copyPendingMarksFrom(context);
0276: if (curLM instanceof LineLayoutManager) {
0277: // curLM is a LineLayoutManager
0278: // set stackLimit for lines (stack limit is now i-p-direction, not b-p-direction!)
0279: childLC
0280: .setStackLimit(new MinOptMax(
0281: getContentAreaIPD()));
0282: childLC.setRefIPD(getContentAreaIPD());
0283: } else {
0284: // curLM is a ?
0285: //childLC.setStackLimit(MinOptMax.subtract(context
0286: // .getStackLimit(), stackSize));
0287: childLC.setStackLimit(context.getStackLimit());
0288: childLC.setRefIPD(referenceIPD);
0289: }
0290:
0291: // get elements from curLM
0292: returnedList = curLM.getNextKnuthElements(childLC,
0293: alignment);
0294: if (contentList.size() == 0
0295: && childLC.isKeepWithPreviousPending()) {
0296: context
0297: .setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING);
0298: childLC
0299: .setFlags(
0300: LayoutContext.KEEP_WITH_PREVIOUS_PENDING,
0301: false);
0302: }
0303: if (returnedList != null
0304: && returnedList.size() == 1
0305: && ((ListElement) returnedList.getFirst())
0306: .isForcedBreak()) {
0307: // a descendant of this block has break-before
0308: /*
0309: if (returnList.size() == 0) {
0310: // the first child (or its first child ...) has
0311: // break-before;
0312: // all this block, including space before, will be put in
0313: // the
0314: // following page
0315: bSpaceBeforeServed = false;
0316: }*/
0317: contentList.addAll(returnedList);
0318:
0319: /* extension: conversione di tutta la sequenza fin'ora ottenuta */
0320: if (bpUnit > 0) {
0321: storedList = contentList;
0322: contentList = createUnitElements(contentList);
0323: }
0324: /* end of extension */
0325:
0326: // "wrap" the Position inside each element
0327: // moving the elements from contentList to returnList
0328: returnedList = new LinkedList();
0329: wrapPositionElements(contentList, returnList);
0330:
0331: return returnList;
0332: } else {
0333: if (prevLM != null) {
0334: // there is a block handled by prevLM
0335: // before the one handled by curLM
0336: if (mustKeepTogether()
0337: || context.isKeepWithNextPending()
0338: || childLC.isKeepWithPreviousPending()) {
0339: // Clear keep pending flag
0340: context.setFlags(
0341: LayoutContext.KEEP_WITH_NEXT_PENDING,
0342: false);
0343: // add an infinite penalty to forbid a break between
0344: // blocks
0345: contentList.add(new BreakElement(new Position(
0346: this ), KnuthElement.INFINITE, context));
0347: } else if (!((ListElement) contentList.getLast())
0348: .isGlue()) {
0349: // add a null penalty to allow a break between blocks
0350: contentList.add(new BreakElement(new Position(
0351: this ), 0, context));
0352: } else {
0353: // the last element in contentList is a glue;
0354: // it is a feasible breakpoint, there is no need to add
0355: // a penalty
0356: log
0357: .warn("glue-type break possibility not handled properly, yet");
0358: //TODO Does this happen? If yes, need to deal with border and padding
0359: //at the break possibility
0360: }
0361: }
0362: if (returnedList == null || returnedList.size() == 0) {
0363: //Avoid NoSuchElementException below (happens with empty blocks)
0364: continue;
0365: }
0366: contentList.addAll(returnedList);
0367: if (((ListElement) returnedList.getLast())
0368: .isForcedBreak()) {
0369: // a descendant of this block has break-after
0370:
0371: /* extension: conversione di tutta la sequenza fin'ora ottenuta */
0372: if (bpUnit > 0) {
0373: storedList = contentList;
0374: contentList = createUnitElements(contentList);
0375: }
0376: /* end of extension */
0377:
0378: returnedList = new LinkedList();
0379: wrapPositionElements(contentList, returnList);
0380:
0381: return returnList;
0382: }
0383: }
0384: // propagate and clear
0385: context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING,
0386: childLC.isKeepWithNextPending());
0387: childLC.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING,
0388: false);
0389: childLC.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING,
0390: false);
0391: prevLM = curLM;
0392: }
0393:
0394: /* Extension: conversione di tutta la sequenza fin'ora ottenuta */
0395: if (bpUnit > 0) {
0396: storedList = contentList;
0397: contentList = createUnitElements(contentList);
0398: }
0399: /* end of extension */
0400:
0401: returnedList = new LinkedList();
0402: if (contentList.size() > 0) {
0403: wrapPositionElements(contentList, returnList);
0404: } else {
0405: //Empty fo:block, zero-length box makes sure the IDs are registered.
0406: returnList.add(new KnuthBox(0,
0407: notifyPos(new Position(this )), true));
0408: }
0409:
0410: addKnuthElementsForBorderPaddingAfter(returnList, true);
0411: addKnuthElementsForSpaceAfter(returnList, alignment);
0412: addKnuthElementsForBreakAfter(returnList, context);
0413:
0414: if (mustKeepWithNext()) {
0415: context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING);
0416: }
0417: if (mustKeepWithPrevious()) {
0418: context.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING);
0419: }
0420:
0421: setFinished(true);
0422:
0423: return returnList;
0424: }
0425:
0426: /**
0427: * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#negotiateBPDAdjustment(int, org.apache.fop.layoutmgr.KnuthElement)
0428: */
0429: public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
0430: /*LF*///log.debug(" BLM.negotiateBPDAdjustment> " + adj);
0431: /*LF*///log.debug(" lastElement e' " + (lastElement.isPenalty()
0432: // ? "penalty" : (lastElement.isGlue() ? "glue" : "box" )));
0433: /*LF*///log.debug(" position e' " + lastElement.getPosition().getClass().getName());
0434: /*LF*///log.debug(" " + (bpUnit > 0 ? "unit" : ""));
0435: Position innerPosition = ((NonLeafPosition) lastElement
0436: .getPosition()).getPosition();
0437:
0438: if (innerPosition == null && lastElement.isGlue()) {
0439: // this adjustment applies to space-before or space-after of this block
0440: if (((KnuthGlue) lastElement).getAdjustmentClass() == SPACE_BEFORE_ADJUSTMENT) {
0441: // this adjustment applies to space-before
0442: adjustedSpaceBefore += adj;
0443: /*LF*///log.debug(" BLM.negotiateBPDAdjustment> spazio prima: " + adj);
0444: } else {
0445: // this adjustment applies to space-after
0446: adjustedSpaceAfter += adj;
0447: /*LF*///log.debug(" BLM.negotiateBPDAdjustment> spazio dopo: " + adj);
0448: }
0449: return adj;
0450: } else if (innerPosition instanceof MappingPosition) {
0451: // this block has block-progression-unit > 0: the adjustment can concern
0452: // - the space-before or space-after of this block,
0453: // - the line number of a descendant of this block
0454: MappingPosition mappingPos = (MappingPosition) innerPosition;
0455: if (lastElement.isGlue()) {
0456: // lastElement is a glue
0457: /*LF*///log.debug(" BLM.negotiateBPDAdjustment> bpunit con glue");
0458: ListIterator storedListIterator = storedList
0459: .listIterator(mappingPos.getFirstIndex());
0460: int newAdjustment = 0;
0461: while (storedListIterator.nextIndex() <= mappingPos
0462: .getLastIndex()) {
0463: KnuthElement storedElement = (KnuthElement) storedListIterator
0464: .next();
0465: if (storedElement.isGlue()) {
0466: newAdjustment += ((BlockLevelLayoutManager) storedElement
0467: .getLayoutManager())
0468: .negotiateBPDAdjustment(adj
0469: - newAdjustment, storedElement);
0470: /*LF*///log.debug(" BLM.negotiateBPDAdjustment> (progressivo) righe: "
0471: // + newAdjustment);
0472: }
0473: }
0474: newAdjustment = (newAdjustment > 0 ? bpUnit
0475: * neededUnits(newAdjustment) : -bpUnit
0476: * neededUnits(-newAdjustment));
0477: return newAdjustment;
0478: } else {
0479: // lastElement is a penalty: this means that the paragraph
0480: // has been split between consecutive pages:
0481: // this may involve a change in the number of lines
0482: /*LF*///log.debug(" BLM.negotiateBPDAdjustment> bpunit con penalty");
0483: KnuthPenalty storedPenalty = (KnuthPenalty) storedList
0484: .get(mappingPos.getLastIndex());
0485: if (storedPenalty.getW() > 0) {
0486: // the original penalty has width > 0
0487: /*LF*///log.debug(" BLM.negotiateBPDAdjustment> chiamata passata");
0488: return ((BlockLevelLayoutManager) storedPenalty
0489: .getLayoutManager())
0490: .negotiateBPDAdjustment(storedPenalty
0491: .getW(),
0492: (KnuthElement) storedPenalty);
0493: } else {
0494: // the original penalty has width = 0
0495: // the adjustment involves only the spaces before and after
0496: /*LF*///log.debug(" BLM.negotiateBPDAdjustment> chiamata gestita");
0497: return adj;
0498: }
0499: }
0500: } else if (innerPosition.getLM() != this ) {
0501: // this adjustment concerns another LM
0502: NonLeafPosition savedPos = (NonLeafPosition) lastElement
0503: .getPosition();
0504: lastElement.setPosition(innerPosition);
0505: int returnValue = ((BlockLevelLayoutManager) lastElement
0506: .getLayoutManager()).negotiateBPDAdjustment(adj,
0507: lastElement);
0508: lastElement.setPosition(savedPos);
0509: /*LF*///log.debug(" BLM.negotiateBPDAdjustment> righe: " + returnValue);
0510: return returnValue;
0511: } else {
0512: // this should never happen
0513: log
0514: .error("BlockLayoutManager.negotiateBPDAdjustment(): unexpected Position");
0515: return 0;
0516: }
0517: }
0518:
0519: /**
0520: * @see BlockLevelLayoutManager#discardSpace(KnuthGlue)
0521: */
0522: public void discardSpace(KnuthGlue spaceGlue) {
0523: //log.debug(" BLM.discardSpace> " + spaceGlue.getPosition().getClass().getName());
0524: Position innerPosition = ((NonLeafPosition) spaceGlue
0525: .getPosition()).getPosition();
0526:
0527: if (innerPosition == null || innerPosition.getLM() == this ) {
0528: // if this block has block-progression-unit > 0, innerPosition can be
0529: // a MappingPosition
0530: // spaceGlue represents space before or space after of this block
0531: if (spaceGlue.getAdjustmentClass() == SPACE_BEFORE_ADJUSTMENT) {
0532: // space-before must be discarded
0533: adjustedSpaceBefore = 0;
0534: foSpaceBefore = new MinOptMax(0);
0535: } else {
0536: // space-after must be discarded
0537: adjustedSpaceAfter = 0;
0538: foSpaceAfter = new MinOptMax(0);
0539: //TODO Why are both cases handled in the same way?
0540: }
0541: } else {
0542: // this element was not created by this BlockLM
0543: NonLeafPosition savedPos = (NonLeafPosition) spaceGlue
0544: .getPosition();
0545: spaceGlue.setPosition(innerPosition);
0546: ((BlockLevelLayoutManager) spaceGlue.getLayoutManager())
0547: .discardSpace(spaceGlue);
0548: spaceGlue.setPosition(savedPos);
0549: }
0550: }
0551:
0552: /**
0553: * @see LayoutManager#getChangedKnuthElements(List, int)
0554: */
0555: public LinkedList getChangedKnuthElements(List oldList,
0556: int alignment) {
0557: /*LF*///log.debug("");
0558: /*LF*///log.debug(" BLM.getChangedKnuthElements> inizio: oldList.size() = "
0559: // + oldList.size());
0560: ListIterator oldListIterator = oldList.listIterator();
0561: KnuthElement returnedElement;
0562: KnuthElement currElement = null;
0563: KnuthElement prevElement = null;
0564: LinkedList returnedList = new LinkedList();
0565: LinkedList returnList = new LinkedList();
0566: int fromIndex = 0;
0567:
0568: // "unwrap" the Positions stored in the elements
0569: KnuthElement oldElement = null;
0570: while (oldListIterator.hasNext()) {
0571: oldElement = (KnuthElement) oldListIterator.next();
0572: Position innerPosition = ((NonLeafPosition) oldElement
0573: .getPosition()).getPosition();
0574: //log.debug(" BLM> unwrapping: "
0575: // + (oldElement.isBox() ? "box " : (oldElement.isGlue() ? "glue " : "penalty"))
0576: // + " creato da " + oldElement.getLayoutManager().getClass().getName());
0577: //log.debug(" BLM> unwrapping: "
0578: // + oldElement.getPosition().getClass().getName());
0579: if (innerPosition != null) {
0580: // oldElement was created by a descendant of this BlockLM
0581: oldElement.setPosition(innerPosition);
0582: } else {
0583: // thisElement was created by this BlockLM
0584: // modify its position in order to recognize it was not created
0585: // by a child
0586: oldElement.setPosition(new Position(this ));
0587: }
0588: }
0589:
0590: // create the iterator
0591: List workList;
0592: if (bpUnit == 0) {
0593: workList = oldList;
0594: } else {
0595: // the storedList must be used instead of oldList;
0596: // find the index of the first element of returnedList
0597: // corresponding to the first element of oldList
0598: oldListIterator = oldList.listIterator();
0599: KnuthElement el = (KnuthElement) oldListIterator.next();
0600: while (!(el.getPosition() instanceof MappingPosition)) {
0601: el = (KnuthElement) oldListIterator.next();
0602: }
0603: int iFirst = ((MappingPosition) el.getPosition())
0604: .getFirstIndex();
0605:
0606: // find the index of the last element of returnedList
0607: // corresponding to the last element of oldList
0608: oldListIterator = oldList.listIterator(oldList.size());
0609: el = (KnuthElement) oldListIterator.previous();
0610: while (!(el.getPosition() instanceof MappingPosition)) {
0611: el = (KnuthElement) oldListIterator.previous();
0612: }
0613: int iLast = ((MappingPosition) el.getPosition())
0614: .getLastIndex();
0615:
0616: //log-debug(" si usa storedList da " + iFirst + " a " + iLast
0617: // + " compresi su " + storedList.size() + " elementi totali");
0618: workList = storedList.subList(iFirst, iLast + 1);
0619: }
0620: ListIterator workListIterator = workList.listIterator();
0621:
0622: //log.debug(" BLM.getChangedKnuthElements> workList.size() = "
0623: // + workList.size() + " da 0 a " + (workList.size() - 1));
0624:
0625: while (workListIterator.hasNext()) {
0626: currElement = (KnuthElement) workListIterator.next();
0627: //log.debug("elemento n. " + workListIterator.previousIndex()
0628: // + " nella workList");
0629: if (prevElement != null
0630: && prevElement.getLayoutManager() != currElement
0631: .getLayoutManager()) {
0632: // prevElement is the last element generated by the same LM
0633: BlockLevelLayoutManager prevLM = (BlockLevelLayoutManager) prevElement
0634: .getLayoutManager();
0635: BlockLevelLayoutManager currLM = (BlockLevelLayoutManager) currElement
0636: .getLayoutManager();
0637: boolean bSomethingAdded = false;
0638: if (prevLM != this ) {
0639: //log.debug(" BLM.getChangedKnuthElements> chiamata da "
0640: // + fromIndex + " a " + workListIterator.previousIndex() + " su "
0641: // + prevLM.getClass().getName());
0642: returnedList.addAll(prevLM.getChangedKnuthElements(
0643: workList.subList(fromIndex,
0644: workListIterator.previousIndex()),
0645: alignment));
0646: bSomethingAdded = true;
0647: } else {
0648: // prevLM == this
0649: // do nothing
0650: //log.debug(" BLM.getChangedKnuthElements> elementi propri, "
0651: // + "ignorati, da " + fromIndex + " a " + workListIterator.previousIndex()
0652: // + " su " + prevLM.getClass().getName());
0653: }
0654: fromIndex = workListIterator.previousIndex();
0655:
0656: /*
0657: * TODO: why are KnuthPenalties added here,
0658: * while in getNextKE they were changed to BreakElements?
0659: */
0660: // there is another block after this one
0661: if (bSomethingAdded
0662: && (this .mustKeepTogether()
0663: || prevLM.mustKeepWithNext() || currLM
0664: .mustKeepWithPrevious())) {
0665: // add an infinite penalty to forbid a break between blocks
0666: returnedList.add(new KnuthPenalty(0,
0667: KnuthElement.INFINITE, false, new Position(
0668: this ), false));
0669: } else if (bSomethingAdded
0670: && !((KnuthElement) returnedList.getLast())
0671: .isGlue()) {
0672: // add a null penalty to allow a break between blocks
0673: returnedList.add(new KnuthPenalty(0, 0, false,
0674: new Position(this ), false));
0675: }
0676: }
0677: prevElement = currElement;
0678: }
0679: if (currElement != null) {
0680: BlockLevelLayoutManager currLM = (BlockLevelLayoutManager) currElement
0681: .getLayoutManager();
0682: if (currLM != this ) {
0683: //log.debug(" BLM.getChangedKnuthElements> chiamata da " + fromIndex
0684: // + " a " + oldList.size() + " su " + currLM.getClass().getName());
0685: returnedList.addAll(currLM.getChangedKnuthElements(
0686: workList.subList(fromIndex, workList.size()),
0687: alignment));
0688: } else {
0689: // currLM == this
0690: // there are no more elements to add
0691: // remove the last penalty added to returnedList
0692: if (returnedList.size() > 0) {
0693: returnedList.removeLast();
0694: }
0695: //log.debug(" BLM.getChangedKnuthElements> elementi propri, ignorati, da "
0696: // + fromIndex + " a " + workList.size());
0697: }
0698: }
0699:
0700: // append elements representing space-before
0701: boolean spaceBeforeIsConditional = true;
0702: if (fobj instanceof org.apache.fop.fo.flow.Block) {
0703: spaceBeforeIsConditional = ((org.apache.fop.fo.flow.Block) fobj)
0704: .getCommonMarginBlock().spaceBefore.getSpace()
0705: .isDiscard();
0706: }
0707: if (bpUnit > 0 || adjustedSpaceBefore != 0) {
0708: if (!spaceBeforeIsConditional) {
0709: // add elements to prevent the glue to be discarded
0710: returnList.add(new KnuthBox(0, new NonLeafPosition(
0711: this , null), false));
0712: returnList.add(new KnuthPenalty(0,
0713: KnuthElement.INFINITE, false,
0714: new NonLeafPosition(this , null), false));
0715: }
0716: if (bpUnit > 0) {
0717: returnList.add(new KnuthGlue(0, 0, 0,
0718: SPACE_BEFORE_ADJUSTMENT, new NonLeafPosition(
0719: this , null), true));
0720: } else {
0721: returnList.add(new KnuthGlue(adjustedSpaceBefore, 0, 0,
0722: SPACE_BEFORE_ADJUSTMENT, new NonLeafPosition(
0723: this , null), true));
0724: }
0725: }
0726:
0727: //log.debug(" BLM.getChangedKnuthElements> intermedio: returnedList.size() = "
0728: // + returnedList.size());
0729:
0730: /* estensione: conversione complessiva */
0731: /*LF*/if (bpUnit > 0) {
0732: /*LF*/storedList = returnedList;
0733: /*LF*/returnedList = createUnitElements(returnedList);
0734: /*LF*/}
0735: /* estensione */
0736:
0737: // "wrap" the Position stored in each element of returnedList
0738: // and add elements to returnList
0739: ListIterator listIter = returnedList.listIterator();
0740: while (listIter.hasNext()) {
0741: returnedElement = (KnuthElement) listIter.next();
0742: returnedElement.setPosition(new NonLeafPosition(this ,
0743: returnedElement.getPosition()));
0744: returnList.add(returnedElement);
0745: }
0746:
0747: // append elements representing space-after
0748: boolean spaceAfterIsConditional = true;
0749: if (fobj instanceof org.apache.fop.fo.flow.Block) {
0750: spaceAfterIsConditional = ((org.apache.fop.fo.flow.Block) fobj)
0751: .getCommonMarginBlock().spaceAfter.getSpace()
0752: .isDiscard();
0753: }
0754: if (bpUnit > 0 || adjustedSpaceAfter != 0) {
0755: if (!spaceAfterIsConditional) {
0756: returnList.add(new KnuthPenalty(0,
0757: KnuthElement.INFINITE, false,
0758: new NonLeafPosition(this , null), false));
0759: }
0760: if (bpUnit > 0) {
0761: returnList.add(new KnuthGlue(0, 0, 0,
0762: SPACE_AFTER_ADJUSTMENT, new NonLeafPosition(
0763: this , null),
0764: (!spaceAfterIsConditional) ? false : true));
0765: } else {
0766: returnList.add(new KnuthGlue(adjustedSpaceAfter, 0, 0,
0767: SPACE_AFTER_ADJUSTMENT, new NonLeafPosition(
0768: this , null),
0769: (!spaceAfterIsConditional) ? false : true));
0770: }
0771: if (!spaceAfterIsConditional) {
0772: returnList.add(new KnuthBox(0, new NonLeafPosition(
0773: this , null), true));
0774: }
0775: }
0776:
0777: //log.debug(" BLM.getChangedKnuthElements> finished: returnList.size() = "
0778: // + returnList.size());
0779: return returnList;
0780: }
0781:
0782: /**
0783: * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether()
0784: */
0785: // default action: ask parentLM
0786: public boolean mustKeepTogether() {
0787: return ((getParent() instanceof BlockLevelLayoutManager && ((BlockLevelLayoutManager) getParent())
0788: .mustKeepTogether()) || (getParent() instanceof InlineLayoutManager && ((InlineLayoutManager) getParent())
0789: .mustKeepTogether()));
0790: }
0791:
0792: /**
0793: * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious()
0794: */
0795: public boolean mustKeepWithPrevious() {
0796: return false;
0797: }
0798:
0799: /**
0800: * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext()
0801: */
0802: public boolean mustKeepWithNext() {
0803: return false;
0804: }
0805:
0806: /**
0807: * Adds the unresolved elements for border and padding to a layout context so break
0808: * possibilities can be properly constructed.
0809: * @param context the layout context
0810: */
0811: protected void addPendingMarks(LayoutContext context) {
0812: CommonBorderPaddingBackground borderAndPadding = getBorderPaddingBackground();
0813: if (borderAndPadding != null) {
0814: if (borderAndPadding.getBorderBeforeWidth(false) > 0) {
0815: context.addPendingBeforeMark(new BorderElement(
0816: getAuxiliaryPosition(),
0817: borderAndPadding.getBorderInfo(
0818: CommonBorderPaddingBackground.BEFORE)
0819: .getWidth(), RelSide.BEFORE, false,
0820: false, this ));
0821: }
0822: if (borderAndPadding.getPaddingBefore(false, this ) > 0) {
0823: context
0824: .addPendingBeforeMark(new PaddingElement(
0825: getAuxiliaryPosition(),
0826: borderAndPadding
0827: .getPaddingLengthProperty(CommonBorderPaddingBackground.BEFORE),
0828: RelSide.BEFORE, false, false, this ));
0829: }
0830: if (borderAndPadding.getBorderAfterWidth(false) > 0) {
0831: context.addPendingAfterMark(new BorderElement(
0832: getAuxiliaryPosition(),
0833: borderAndPadding.getBorderInfo(
0834: CommonBorderPaddingBackground.AFTER)
0835: .getWidth(), RelSide.AFTER, false,
0836: false, this ));
0837: }
0838: if (borderAndPadding.getPaddingAfter(false, this ) > 0) {
0839: context
0840: .addPendingAfterMark(new PaddingElement(
0841: getAuxiliaryPosition(),
0842: borderAndPadding
0843: .getPaddingLengthProperty(CommonBorderPaddingBackground.AFTER),
0844: RelSide.AFTER, false, false, this ));
0845: }
0846: }
0847: }
0848:
0849: /** @return the border, padding and background info structure */
0850: private CommonBorderPaddingBackground getBorderPaddingBackground() {
0851: if (fobj instanceof org.apache.fop.fo.flow.Block) {
0852: return ((org.apache.fop.fo.flow.Block) fobj)
0853: .getCommonBorderPaddingBackground();
0854: } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
0855: return ((org.apache.fop.fo.flow.BlockContainer) fobj)
0856: .getCommonBorderPaddingBackground();
0857: } else if (fobj instanceof org.apache.fop.fo.flow.ListBlock) {
0858: return ((org.apache.fop.fo.flow.ListBlock) fobj)
0859: .getCommonBorderPaddingBackground();
0860: } else if (fobj instanceof org.apache.fop.fo.flow.ListItem) {
0861: return ((org.apache.fop.fo.flow.ListItem) fobj)
0862: .getCommonBorderPaddingBackground();
0863: } else if (fobj instanceof org.apache.fop.fo.flow.Table) {
0864: return ((org.apache.fop.fo.flow.Table) fobj)
0865: .getCommonBorderPaddingBackground();
0866: } else {
0867: return null;
0868: }
0869: }
0870:
0871: /** @return the space-before property */
0872: private SpaceProperty getSpaceBeforeProperty() {
0873: if (fobj instanceof org.apache.fop.fo.flow.Block) {
0874: return ((org.apache.fop.fo.flow.Block) fobj)
0875: .getCommonMarginBlock().spaceBefore;
0876: } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
0877: return ((org.apache.fop.fo.flow.BlockContainer) fobj)
0878: .getCommonMarginBlock().spaceBefore;
0879: } else if (fobj instanceof org.apache.fop.fo.flow.ListBlock) {
0880: return ((org.apache.fop.fo.flow.ListBlock) fobj)
0881: .getCommonMarginBlock().spaceBefore;
0882: } else if (fobj instanceof org.apache.fop.fo.flow.ListItem) {
0883: return ((org.apache.fop.fo.flow.ListItem) fobj)
0884: .getCommonMarginBlock().spaceBefore;
0885: } else if (fobj instanceof org.apache.fop.fo.flow.Table) {
0886: return ((org.apache.fop.fo.flow.Table) fobj)
0887: .getCommonMarginBlock().spaceBefore;
0888: } else {
0889: return null;
0890: }
0891: }
0892:
0893: /** @return the space-after property */
0894: private SpaceProperty getSpaceAfterProperty() {
0895: if (fobj instanceof org.apache.fop.fo.flow.Block) {
0896: return ((org.apache.fop.fo.flow.Block) fobj)
0897: .getCommonMarginBlock().spaceAfter;
0898: } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
0899: return ((org.apache.fop.fo.flow.BlockContainer) fobj)
0900: .getCommonMarginBlock().spaceAfter;
0901: } else if (fobj instanceof org.apache.fop.fo.flow.ListBlock) {
0902: return ((org.apache.fop.fo.flow.ListBlock) fobj)
0903: .getCommonMarginBlock().spaceAfter;
0904: } else if (fobj instanceof org.apache.fop.fo.flow.ListItem) {
0905: return ((org.apache.fop.fo.flow.ListItem) fobj)
0906: .getCommonMarginBlock().spaceAfter;
0907: } else if (fobj instanceof org.apache.fop.fo.flow.Table) {
0908: return ((org.apache.fop.fo.flow.Table) fobj)
0909: .getCommonMarginBlock().spaceAfter;
0910: } else {
0911: return null;
0912: }
0913: }
0914:
0915: /**
0916: * Creates Knuth elements for before border padding and adds them to the return list.
0917: * @param returnList return list to add the additional elements to
0918: * @param isFirst true if this is the first time a layout manager instance needs to generate
0919: * border and padding
0920: */
0921: protected void addKnuthElementsForBorderPaddingBefore(
0922: LinkedList returnList, boolean isFirst) {
0923: //Border and Padding (before)
0924: CommonBorderPaddingBackground borderAndPadding = getBorderPaddingBackground();
0925: if (borderAndPadding != null) {
0926: if (borderAndPadding.getBorderBeforeWidth(false) > 0) {
0927: returnList.add(new BorderElement(
0928: getAuxiliaryPosition(),
0929: borderAndPadding.getBorderInfo(
0930: CommonBorderPaddingBackground.BEFORE)
0931: .getWidth(), RelSide.BEFORE, isFirst,
0932: false, this ));
0933: }
0934: if (borderAndPadding.getPaddingBefore(false, this ) > 0) {
0935: returnList
0936: .add(new PaddingElement(
0937: getAuxiliaryPosition(),
0938: borderAndPadding
0939: .getPaddingLengthProperty(CommonBorderPaddingBackground.BEFORE),
0940: RelSide.BEFORE, isFirst, false, this ));
0941: }
0942: }
0943: }
0944:
0945: /**
0946: * Creates Knuth elements for after border padding and adds them to the return list.
0947: * @param returnList return list to add the additional elements to
0948: * @param isLast true if this is the last time a layout manager instance needs to generate
0949: * border and padding
0950: */
0951: protected void addKnuthElementsForBorderPaddingAfter(
0952: LinkedList returnList, boolean isLast) {
0953: //Border and Padding (after)
0954: CommonBorderPaddingBackground borderAndPadding = getBorderPaddingBackground();
0955: if (borderAndPadding != null) {
0956: if (borderAndPadding.getPaddingAfter(false, this ) > 0) {
0957: returnList
0958: .add(new PaddingElement(
0959: getAuxiliaryPosition(),
0960: borderAndPadding
0961: .getPaddingLengthProperty(CommonBorderPaddingBackground.AFTER),
0962: RelSide.AFTER, false, isLast, this ));
0963: }
0964: if (borderAndPadding.getBorderAfterWidth(false) > 0) {
0965: returnList.add(new BorderElement(
0966: getAuxiliaryPosition(),
0967: borderAndPadding.getBorderInfo(
0968: CommonBorderPaddingBackground.AFTER)
0969: .getWidth(), RelSide.AFTER, false,
0970: isLast, this ));
0971: }
0972: }
0973: }
0974:
0975: /**
0976: * Creates Knuth elements for break-before and adds them to the return list.
0977: * @param returnList return list to add the additional elements to
0978: * @param context the layout context
0979: * @return true if an element has been added due to a break-before.
0980: */
0981: protected boolean addKnuthElementsForBreakBefore(
0982: LinkedList returnList, LayoutContext context) {
0983: int breakBefore = -1;
0984: if (fobj instanceof org.apache.fop.fo.flow.Block) {
0985: breakBefore = ((org.apache.fop.fo.flow.Block) fobj)
0986: .getBreakBefore();
0987: } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
0988: breakBefore = ((org.apache.fop.fo.flow.BlockContainer) fobj)
0989: .getBreakBefore();
0990: } else if (fobj instanceof org.apache.fop.fo.flow.ListBlock) {
0991: breakBefore = ((org.apache.fop.fo.flow.ListBlock) fobj)
0992: .getBreakBefore();
0993: } else if (fobj instanceof org.apache.fop.fo.flow.ListItem) {
0994: breakBefore = ((org.apache.fop.fo.flow.ListItem) fobj)
0995: .getBreakBefore();
0996: } else if (fobj instanceof org.apache.fop.fo.flow.Table) {
0997: breakBefore = ((org.apache.fop.fo.flow.Table) fobj)
0998: .getBreakBefore();
0999: }
1000: if (breakBefore == EN_PAGE || breakBefore == EN_COLUMN
1001: || breakBefore == EN_EVEN_PAGE
1002: || breakBefore == EN_ODD_PAGE) {
1003: // return a penalty element, representing a forced page break
1004: returnList.add(new BreakElement(getAuxiliaryPosition(), 0,
1005: -KnuthElement.INFINITE, breakBefore, context));
1006: return true;
1007: } else {
1008: return false;
1009: }
1010: }
1011:
1012: /**
1013: * Creates Knuth elements for break-after and adds them to the return list.
1014: * @param returnList return list to add the additional elements to
1015: * @param context the layout context
1016: * @return true if an element has been added due to a break-after.
1017: */
1018: protected boolean addKnuthElementsForBreakAfter(
1019: LinkedList returnList, LayoutContext context) {
1020: int breakAfter = -1;
1021: if (fobj instanceof org.apache.fop.fo.flow.Block) {
1022: breakAfter = ((org.apache.fop.fo.flow.Block) fobj)
1023: .getBreakAfter();
1024: } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
1025: breakAfter = ((org.apache.fop.fo.flow.BlockContainer) fobj)
1026: .getBreakAfter();
1027: } else if (fobj instanceof org.apache.fop.fo.flow.ListBlock) {
1028: breakAfter = ((org.apache.fop.fo.flow.ListBlock) fobj)
1029: .getBreakAfter();
1030: } else if (fobj instanceof org.apache.fop.fo.flow.ListItem) {
1031: breakAfter = ((org.apache.fop.fo.flow.ListItem) fobj)
1032: .getBreakAfter();
1033: } else if (fobj instanceof org.apache.fop.fo.flow.Table) {
1034: breakAfter = ((org.apache.fop.fo.flow.Table) fobj)
1035: .getBreakAfter();
1036: }
1037: if (breakAfter == EN_PAGE || breakAfter == EN_COLUMN
1038: || breakAfter == EN_EVEN_PAGE
1039: || breakAfter == EN_ODD_PAGE) {
1040: // add a penalty element, representing a forced page break
1041: returnList.add(new BreakElement(getAuxiliaryPosition(), 0,
1042: -KnuthElement.INFINITE, breakAfter, context));
1043: return true;
1044: } else {
1045: return false;
1046: }
1047: }
1048:
1049: /**
1050: * Creates Knuth elements for space-before and adds them to the return list.
1051: * @param returnList return list to add the additional elements to
1052: * @param alignment vertical alignment
1053: */
1054: protected void addKnuthElementsForSpaceBefore(LinkedList returnList/*,
1055: Position returnPosition*/, int alignment) {
1056: SpaceProperty spaceBefore = getSpaceBeforeProperty();
1057: // append elements representing space-before
1058: if (spaceBefore != null
1059: && !(spaceBefore.getMinimum(this ).getLength().getValue(
1060: this ) == 0 && spaceBefore.getMaximum(this )
1061: .getLength().getValue(this ) == 0)) {
1062: returnList.add(new SpaceElement(getAuxiliaryPosition(),
1063: spaceBefore, RelSide.BEFORE, true, false, this ));
1064: }
1065: /*
1066: if (bpUnit > 0
1067: || spaceBefore != null
1068: && !(spaceBefore.getMinimum(this).getLength().getValue(this) == 0
1069: && spaceBefore.getMaximum(this).getLength().getValue(this) == 0)) {
1070: if (spaceBefore != null && !spaceBefore.getSpace().isDiscard()) {
1071: // add elements to prevent the glue to be discarded
1072: returnList.add(new KnuthBox(0, getAuxiliaryPosition(), false));
1073: returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE,
1074: false, getAuxiliaryPosition(), false));
1075: }
1076: if (bpUnit > 0) {
1077: returnList.add(new KnuthGlue(0, 0, 0,
1078: BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT,
1079: getAuxiliaryPosition(), true));
1080: } else { //if (alignment == EN_JUSTIFY) {
1081: returnList.add(new KnuthGlue(
1082: spaceBefore.getOptimum(this).getLength().getValue(this),
1083: spaceBefore.getMaximum(this).getLength().getValue(this)
1084: - spaceBefore.getOptimum(this).getLength().getValue(this),
1085: spaceBefore.getOptimum(this).getLength().getValue(this)
1086: - spaceBefore.getMinimum(this).getLength().getValue(this),
1087: BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT,
1088: getAuxiliaryPosition(), true));
1089: // } else {
1090: // returnList.add(new KnuthGlue(
1091: // spaceBefore.getOptimum().getLength().getValue(this),
1092: // 0, 0, BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT,
1093: // returnPosition, true));
1094: }
1095: }*/
1096: }
1097:
1098: /**
1099: * Creates Knuth elements for space-after and adds them to the return list.
1100: * @param returnList return list to add the additional elements to
1101: * @param alignment vertical alignment
1102: */
1103: protected void addKnuthElementsForSpaceAfter(
1104: LinkedList returnList/*, Position returnPosition*/,
1105: int alignment) {
1106: SpaceProperty spaceAfter = getSpaceAfterProperty();
1107: // append elements representing space-after
1108: if (spaceAfter != null
1109: && !(spaceAfter.getMinimum(this ).getLength().getValue(
1110: this ) == 0 && spaceAfter.getMaximum(this )
1111: .getLength().getValue(this ) == 0)) {
1112: returnList.add(new SpaceElement(getAuxiliaryPosition(),
1113: spaceAfter, RelSide.AFTER, false, true, this ));
1114: }
1115: /*
1116: if (bpUnit > 0
1117: || spaceAfter != null
1118: && !(spaceAfter.getMinimum(this).getLength().getValue(this) == 0
1119: && spaceAfter.getMaximum(this).getLength().getValue(this) == 0)) {
1120: if (spaceAfter != null && !spaceAfter.getSpace().isDiscard()) {
1121: returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE,
1122: false, getAuxiliaryPosition(), false));
1123: }
1124: if (bpUnit > 0) {
1125: returnList.add(new KnuthGlue(0, 0, 0,
1126: BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT,
1127: getAuxiliaryPosition(), true));
1128: } else { //if (alignment == EN_JUSTIFY) {
1129: returnList.add(new KnuthGlue(
1130: spaceAfter.getOptimum(this).getLength().getValue(this),
1131: spaceAfter.getMaximum(this).getLength().getValue(this)
1132: - spaceAfter.getOptimum(this).getLength().getValue(this),
1133: spaceAfter.getOptimum(this).getLength().getValue(this)
1134: - spaceAfter.getMinimum(this).getLength().getValue(this),
1135: BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT, getAuxiliaryPosition(),
1136: (!spaceAfter.getSpace().isDiscard()) ? false : true));
1137: // } else {
1138: // returnList.add(new KnuthGlue(
1139: // spaceAfter.getOptimum().getLength().getValue(this), 0, 0,
1140: // BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT, returnPosition,
1141: // (!spaceAfter.getSpace().isDiscard()) ? false : true));
1142: }
1143: if (spaceAfter != null && !spaceAfter.getSpace().isDiscard()) {
1144: returnList.add(new KnuthBox(0, getAuxiliaryPosition(), true));
1145: }
1146: }*/
1147: }
1148:
1149: protected LinkedList createUnitElements(LinkedList oldList) {
1150: //log.debug("Start conversion: " + oldList.size()
1151: // + " elements, space-before.min=" + layoutProps.spaceBefore.getSpace().min
1152: // + " space-after.min=" + layoutProps.spaceAfter.getSpace().min);
1153: // add elements at the beginning and at the end of oldList
1154: // representing minimum spaces
1155: LayoutManager lm = ((KnuthElement) oldList.getFirst())
1156: .getLayoutManager();
1157: boolean bAddedBoxBefore = false;
1158: boolean bAddedBoxAfter = false;
1159: if (adjustedSpaceBefore > 0) {
1160: oldList.addFirst(new KnuthBox(adjustedSpaceBefore,
1161: new Position(lm), true));
1162: bAddedBoxBefore = true;
1163: }
1164: if (adjustedSpaceAfter > 0) {
1165: oldList.addLast(new KnuthBox(adjustedSpaceAfter,
1166: new Position(lm), true));
1167: bAddedBoxAfter = true;
1168: }
1169:
1170: MinOptMax totalLength = new MinOptMax(0);
1171: MinOptMax totalUnits = new MinOptMax(0);
1172: LinkedList newList = new LinkedList();
1173:
1174: //log.debug(" Prima scansione");
1175: // scan the list once to compute total min, opt and max length
1176: ListIterator oldListIterator = oldList.listIterator();
1177: while (oldListIterator.hasNext()) {
1178: KnuthElement element = (KnuthElement) oldListIterator
1179: .next();
1180: if (element.isBox()) {
1181: totalLength.add(new MinOptMax(element.getW()));
1182: //log.debug("box " + element.getW());
1183: } else if (element.isGlue()) {
1184: totalLength.min -= ((KnuthGlue) element).getZ();
1185: totalLength.max += ((KnuthGlue) element).getY();
1186: //leafValue = ((LeafPosition) element.getPosition()).getLeafPos();
1187: //log.debug("glue " + element.getW() + " + "
1188: // + ((KnuthGlue) element).getY() + " - " + ((KnuthGlue) element).getZ());
1189: } else {
1190: //log.debug((((KnuthPenalty)element).getP() == KnuthElement.INFINITE
1191: // ? "PENALTY " : "penalty ") + element.getW());
1192: }
1193: }
1194: // compute the total amount of "units"
1195: totalUnits = new MinOptMax(neededUnits(totalLength.min),
1196: neededUnits(totalLength.opt),
1197: neededUnits(totalLength.max));
1198: //log.debug(" totalLength= " + totalLength);
1199: //log.debug(" unita'= " + totalUnits);
1200:
1201: //log.debug(" Seconda scansione");
1202: // scan the list once more, stopping at every breaking point
1203: // in order to compute partial min, opt and max length
1204: // and create the new elements
1205: oldListIterator = oldList.listIterator();
1206: boolean bPrevIsBox = false;
1207: MinOptMax lengthBeforeBreak = new MinOptMax(0);
1208: MinOptMax lengthAfterBreak = (MinOptMax) totalLength.clone();
1209: MinOptMax unitsBeforeBreak;
1210: MinOptMax unitsAfterBreak;
1211: MinOptMax unsuppressibleUnits = new MinOptMax(0);
1212: int firstIndex = 0;
1213: int lastIndex = -1;
1214: while (oldListIterator.hasNext()) {
1215: KnuthElement element = (KnuthElement) oldListIterator
1216: .next();
1217: lastIndex++;
1218: if (element.isBox()) {
1219: lengthBeforeBreak.add(new MinOptMax(element.getW()));
1220: lengthAfterBreak
1221: .subtract(new MinOptMax(element.getW()));
1222: bPrevIsBox = true;
1223: } else if (element.isGlue()) {
1224: lengthBeforeBreak.min -= ((KnuthGlue) element).getZ();
1225: lengthAfterBreak.min += ((KnuthGlue) element).getZ();
1226: lengthBeforeBreak.max += ((KnuthGlue) element).getY();
1227: lengthAfterBreak.max -= ((KnuthGlue) element).getY();
1228: bPrevIsBox = false;
1229: } else {
1230: lengthBeforeBreak.add(new MinOptMax(element.getW()));
1231: bPrevIsBox = false;
1232: }
1233:
1234: // create the new elements
1235: if (element.isPenalty()
1236: && ((KnuthPenalty) element).getP() < KnuthElement.INFINITE
1237: || element.isGlue() && bPrevIsBox
1238: || !oldListIterator.hasNext()) {
1239: // suppress elements after the breaking point
1240: int iStepsForward = 0;
1241: while (oldListIterator.hasNext()) {
1242: KnuthElement el = (KnuthElement) oldListIterator
1243: .next();
1244: iStepsForward++;
1245: if (el.isGlue()) {
1246: // suppressed glue
1247: lengthAfterBreak.min += ((KnuthGlue) el).getZ();
1248: lengthAfterBreak.max -= ((KnuthGlue) el).getY();
1249: } else if (el.isPenalty()) {
1250: // suppressed penalty, do nothing
1251: } else {
1252: // box, end of suppressions
1253: break;
1254: }
1255: }
1256: // compute the partial amount of "units" before and after the break
1257: unitsBeforeBreak = new MinOptMax(
1258: neededUnits(lengthBeforeBreak.min),
1259: neededUnits(lengthBeforeBreak.opt),
1260: neededUnits(lengthBeforeBreak.max));
1261: unitsAfterBreak = new MinOptMax(
1262: neededUnits(lengthAfterBreak.min),
1263: neededUnits(lengthAfterBreak.opt),
1264: neededUnits(lengthAfterBreak.max));
1265:
1266: // rewind the iterator and lengthAfterBreak
1267: for (int i = 0; i < iStepsForward; i++) {
1268: KnuthElement el = (KnuthElement) oldListIterator
1269: .previous();
1270: if (el.isGlue()) {
1271: lengthAfterBreak.min -= ((KnuthGlue) el).getZ();
1272: lengthAfterBreak.max += ((KnuthGlue) el).getY();
1273: }
1274: }
1275:
1276: // compute changes in length, stretch and shrink
1277: int uLengthChange = unitsBeforeBreak.opt
1278: + unitsAfterBreak.opt - totalUnits.opt;
1279: int uStretchChange = (unitsBeforeBreak.max
1280: + unitsAfterBreak.max - totalUnits.max)
1281: - (unitsBeforeBreak.opt + unitsAfterBreak.opt - totalUnits.opt);
1282: int uShrinkChange = (unitsBeforeBreak.opt
1283: + unitsAfterBreak.opt - totalUnits.opt)
1284: - (unitsBeforeBreak.min + unitsAfterBreak.min - totalUnits.min);
1285:
1286: // compute the number of normal, stretch and shrink unit
1287: // that must be added to the new sequence
1288: int uNewNormal = unitsBeforeBreak.opt
1289: - unsuppressibleUnits.opt;
1290: int uNewStretch = (unitsBeforeBreak.max - unitsBeforeBreak.opt)
1291: - (unsuppressibleUnits.max - unsuppressibleUnits.opt);
1292: int uNewShrink = (unitsBeforeBreak.opt - unitsBeforeBreak.min)
1293: - (unsuppressibleUnits.opt - unsuppressibleUnits.min);
1294:
1295: //log.debug("("
1296: // + unsuppressibleUnits.min + "-" + unsuppressibleUnits.opt + "-"
1297: // + unsuppressibleUnits.max + ") "
1298: // + " -> " + unitsBeforeBreak.min + "-" + unitsBeforeBreak.opt + "-"
1299: // + unitsBeforeBreak.max
1300: // + " + " + unitsAfterBreak.min + "-" + unitsAfterBreak.opt + "-"
1301: // + unitsAfterBreak.max
1302: // + (uLengthChange != 0 ? " [length " + uLengthChange + "] " : "")
1303: // + (uStretchChange != 0 ? " [stretch " + uStretchChange + "] " : "")
1304: // + (uShrinkChange != 0 ? " [shrink " + uShrinkChange + "]" : ""));
1305:
1306: // create the MappingPosition which will be stored in the new elements
1307: // correct firstIndex and lastIndex
1308: int firstIndexCorrection = 0;
1309: int lastIndexCorrection = 0;
1310: if (bAddedBoxBefore) {
1311: if (firstIndex != 0) {
1312: firstIndexCorrection++;
1313: }
1314: lastIndexCorrection++;
1315: }
1316: if (bAddedBoxAfter && lastIndex == (oldList.size() - 1)) {
1317: lastIndexCorrection++;
1318: }
1319: MappingPosition mappingPos = new MappingPosition(this ,
1320: firstIndex - firstIndexCorrection, lastIndex
1321: - lastIndexCorrection);
1322:
1323: // new box
1324: newList.add(new KnuthBox((uNewNormal - uLengthChange)
1325: * bpUnit, mappingPos, false));
1326: unsuppressibleUnits.add(new MinOptMax(uNewNormal
1327: - uLengthChange));
1328: //log.debug(" box " + (uNewNormal - uLengthChange));
1329:
1330: // new infinite penalty, glue and box, if necessary
1331: if (uNewStretch - uStretchChange > 0
1332: || uNewShrink - uShrinkChange > 0) {
1333: int iStretchUnits = (uNewStretch - uStretchChange > 0 ? (uNewStretch - uStretchChange)
1334: : 0);
1335: int iShrinkUnits = (uNewShrink - uShrinkChange > 0 ? (uNewShrink - uShrinkChange)
1336: : 0);
1337: newList.add(new KnuthPenalty(0,
1338: KnuthElement.INFINITE, false, mappingPos,
1339: false));
1340: newList.add(new KnuthGlue(0,
1341: iStretchUnits * bpUnit, iShrinkUnits
1342: * bpUnit, LINE_NUMBER_ADJUSTMENT,
1343: mappingPos, false));
1344: //log.debug(" PENALTY");
1345: //log.debug(" glue 0 " + iStretchUnits + " " + iShrinkUnits);
1346: unsuppressibleUnits.max += iStretchUnits;
1347: unsuppressibleUnits.min -= iShrinkUnits;
1348: if (!oldListIterator.hasNext()) {
1349: newList.add(new KnuthBox(0, mappingPos, false));
1350: //log.debug(" box 0");
1351: }
1352: }
1353:
1354: // new breaking sequence
1355: if (uStretchChange != 0 || uShrinkChange != 0) {
1356: // new infinite penalty, glue, penalty and glue
1357: newList.add(new KnuthPenalty(0,
1358: KnuthElement.INFINITE, false, mappingPos,
1359: false));
1360: newList.add(new KnuthGlue(0, uStretchChange
1361: * bpUnit, uShrinkChange * bpUnit,
1362: LINE_NUMBER_ADJUSTMENT, mappingPos, false));
1363: newList.add(new KnuthPenalty(
1364: uLengthChange * bpUnit, 0, false, element
1365: .getPosition(), false));
1366: newList.add(new KnuthGlue(0, -uStretchChange
1367: * bpUnit, -uShrinkChange * bpUnit,
1368: LINE_NUMBER_ADJUSTMENT, mappingPos, false));
1369: //log.debug(" PENALTY");
1370: //log.debug(" glue 0 " + uStretchChange + " " + uShrinkChange);
1371: //log.debug(" penalty " + uLengthChange + " * unit");
1372: //log.debug(" glue 0 " + (- uStretchChange) + " "
1373: // + (- uShrinkChange));
1374: } else if (oldListIterator.hasNext()) {
1375: // new penalty
1376: newList.add(new KnuthPenalty(
1377: uLengthChange * bpUnit, 0, false,
1378: mappingPos, false));
1379: //log.debug(" penalty " + uLengthChange + " * unit");
1380: }
1381: // update firstIndex
1382: firstIndex = lastIndex + 1;
1383: }
1384:
1385: if (element.isPenalty()) {
1386: lengthBeforeBreak.add(new MinOptMax(-element.getW()));
1387: }
1388:
1389: }
1390:
1391: // remove elements at the beginning and at the end of oldList
1392: // representing minimum spaces
1393: if (adjustedSpaceBefore > 0) {
1394: oldList.removeFirst();
1395: }
1396: if (adjustedSpaceAfter > 0) {
1397: oldList.removeLast();
1398: }
1399:
1400: // if space-before.conditionality is "discard", correct newList
1401: boolean correctFirstElement = false;
1402: if (fobj instanceof org.apache.fop.fo.flow.Block) {
1403: correctFirstElement = ((org.apache.fop.fo.flow.Block) fobj)
1404: .getCommonMarginBlock().spaceBefore.getSpace()
1405: .isDiscard();
1406: }
1407: if (correctFirstElement) {
1408: // remove the wrong element
1409: KnuthBox wrongBox = (KnuthBox) newList.removeFirst();
1410: // if this paragraph is at the top of a page, the space before
1411: // must be ignored; compute the length change
1412: int decreasedLength = (neededUnits(totalLength.opt) - neededUnits(totalLength.opt
1413: - adjustedSpaceBefore))
1414: * bpUnit;
1415: // insert the correct elements
1416: newList.addFirst(new KnuthBox(wrongBox.getW()
1417: - decreasedLength, wrongBox.getPosition(), false));
1418: newList.addFirst(new KnuthGlue(decreasedLength, 0, 0,
1419: SPACE_BEFORE_ADJUSTMENT, wrongBox.getPosition(),
1420: false));
1421: //log.debug(" rimosso box " + neededUnits(wrongBox.getW()));
1422: //log.debug(" aggiunto glue " + neededUnits(decreasedLength) + " 0 0");
1423: //log.debug(" aggiunto box " + neededUnits(
1424: // wrongBox.getW() - decreasedLength));
1425: }
1426:
1427: // if space-after.conditionality is "discard", correct newList
1428: boolean correctLastElement = false;
1429: if (fobj instanceof org.apache.fop.fo.flow.Block) {
1430: correctLastElement = ((org.apache.fop.fo.flow.Block) fobj)
1431: .getCommonMarginBlock().spaceAfter.getSpace()
1432: .isDiscard();
1433: }
1434: if (correctLastElement) {
1435: // remove the wrong element
1436: KnuthBox wrongBox = (KnuthBox) newList.removeLast();
1437: // if the old sequence is box(h) penalty(inf) glue(x,y,z) box(0)
1438: // (it cannot be parted and has some stretch or shrink)
1439: // the wrong box is the first one, not the last one
1440: LinkedList preserveList = new LinkedList();
1441: if (wrongBox.getW() == 0) {
1442: preserveList.add(wrongBox);
1443: preserveList.addFirst((KnuthGlue) newList.removeLast());
1444: preserveList.addFirst((KnuthPenalty) newList
1445: .removeLast());
1446: wrongBox = (KnuthBox) newList.removeLast();
1447: }
1448:
1449: // if this paragraph is at the bottom of a page, the space after
1450: // must be ignored; compute the length change
1451: int decreasedLength = (neededUnits(totalLength.opt) - neededUnits(totalLength.opt
1452: - adjustedSpaceAfter))
1453: * bpUnit;
1454: // insert the correct box
1455: newList.addLast(new KnuthBox(wrongBox.getW()
1456: - decreasedLength, wrongBox.getPosition(), false));
1457: // add preserved elements
1458: if (preserveList.size() > 0) {
1459: newList.addAll(preserveList);
1460: }
1461: // insert the correct glue
1462: newList.addLast(new KnuthGlue(decreasedLength, 0, 0,
1463: SPACE_AFTER_ADJUSTMENT, wrongBox.getPosition(),
1464: false));
1465: //log.debug(" rimosso box " + neededUnits(wrongBox.getW()));
1466: //log.debug(" aggiunto box " + neededUnits(
1467: // wrongBox.getW() - decreasedLength));
1468: //log.debug(" aggiunto glue " + neededUnits(decreasedLength) + " 0 0");
1469: }
1470:
1471: return newList;
1472: }
1473:
1474: protected static class StackingIter extends PositionIterator {
1475: StackingIter(Iterator parentIter) {
1476: super (parentIter);
1477: }
1478:
1479: protected LayoutManager getLM(Object nextObj) {
1480: return ((Position) nextObj).getLM();
1481: }
1482:
1483: protected Position getPos(Object nextObj) {
1484: return ((Position) nextObj);
1485: }
1486: }
1487:
1488: protected static class MappingPosition extends Position {
1489: private int iFirstIndex;
1490: private int iLastIndex;
1491:
1492: public MappingPosition(LayoutManager lm, int first, int last) {
1493: super (lm);
1494: iFirstIndex = first;
1495: iLastIndex = last;
1496: }
1497:
1498: public int getFirstIndex() {
1499: return iFirstIndex;
1500: }
1501:
1502: public int getLastIndex() {
1503: return iLastIndex;
1504: }
1505: }
1506:
1507: /**
1508: * "wrap" the Position inside each element moving the elements from
1509: * SourceList to targetList
1510: * @param sourceList source list
1511: * @param targetList target list receiving the wrapped position elements
1512: */
1513: protected void wrapPositionElements(List sourceList, List targetList) {
1514: wrapPositionElements(sourceList, targetList, false);
1515: }
1516:
1517: /**
1518: * "wrap" the Position inside each element moving the elements from
1519: * SourceList to targetList
1520: * @param sourceList source list
1521: * @param targetList target list receiving the wrapped position elements
1522: * @param force if true, every Position is wrapped regardless of its LM of origin
1523: */
1524: protected void wrapPositionElements(List sourceList,
1525: List targetList, boolean force) {
1526:
1527: ListIterator listIter = sourceList.listIterator();
1528: while (listIter.hasNext()) {
1529: ListElement tempElement;
1530: tempElement = (ListElement) listIter.next();
1531: if (force || tempElement.getLayoutManager() != this ) {
1532: tempElement.setPosition(notifyPos(new NonLeafPosition(
1533: this , tempElement.getPosition())));
1534: }
1535: targetList.add(tempElement);
1536: }
1537: }
1538:
1539: /** @return the sum of start-indent and end-indent */
1540: protected int getIPIndents() {
1541: return startIndent + endIndent;
1542: }
1543:
1544: /**
1545: * Returns the IPD of the content area
1546: * @return the IPD of the content area
1547: */
1548: public int getContentAreaIPD() {
1549: return contentAreaIPD;
1550: }
1551:
1552: /**
1553: * Sets the IPD of the content area
1554: * @param contentAreaIPD the IPD of the content area
1555: */
1556: protected void setContentAreaIPD(int contentAreaIPD) {
1557: this .contentAreaIPD = contentAreaIPD;
1558: }
1559:
1560: /**
1561: * Returns the BPD of the content area
1562: * @return the BPD of the content area
1563: */
1564: public int getContentAreaBPD() {
1565: return -1;
1566: }
1567:
1568: }
|