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: BlockContainerLayoutManager.java 525253 2007-04-03 19:40:14Z spepping $ */
0019:
0020: package org.apache.fop.layoutmgr;
0021:
0022: import java.util.LinkedList;
0023: import java.util.List;
0024: import java.util.ListIterator;
0025: import java.awt.Point;
0026: import java.awt.geom.Rectangle2D;
0027:
0028: import org.apache.commons.logging.Log;
0029: import org.apache.commons.logging.LogFactory;
0030: import org.apache.fop.area.Area;
0031: import org.apache.fop.area.BlockViewport;
0032: import org.apache.fop.area.Block;
0033: import org.apache.fop.area.Trait;
0034: import org.apache.fop.fo.FONode;
0035: import org.apache.fop.fo.flow.BlockContainer;
0036: import org.apache.fop.fo.properties.CommonAbsolutePosition;
0037: import org.apache.fop.layoutmgr.inline.InlineLayoutManager;
0038: import org.apache.fop.area.CTM;
0039: import org.apache.fop.datatypes.FODimension;
0040: import org.apache.fop.datatypes.Length;
0041: import org.apache.fop.traits.MinOptMax;
0042: import org.apache.fop.traits.SpaceVal;
0043:
0044: /**
0045: * LayoutManager for a block-container FO.
0046: */
0047: public class BlockContainerLayoutManager extends
0048: BlockStackingLayoutManager implements
0049: ConditionalElementListener {
0050:
0051: /**
0052: * logging instance
0053: */
0054: private static Log log = LogFactory
0055: .getLog(BlockContainerLayoutManager.class);
0056:
0057: private BlockViewport viewportBlockArea;
0058: private Block referenceArea;
0059:
0060: private CommonAbsolutePosition abProps;
0061: private FODimension relDims;
0062: private CTM absoluteCTM;
0063: private Length width;
0064: private Length height;
0065: //private int vpContentIPD;
0066: private int vpContentBPD;
0067:
0068: // When viewport should grow with the content.
0069: private boolean autoHeight = true;
0070:
0071: /* holds the (one-time use) fo:block space-before
0072: and -after properties. Large fo:blocks are split
0073: into multiple Area.Blocks to accomodate the subsequent
0074: regions (pages) they are placed on. space-before
0075: is applied at the beginning of the first
0076: Block and space-after at the end of the last Block
0077: used in rendering the fo:block.
0078: */
0079: //TODO space-before|after: handle space-resolution rules
0080: private MinOptMax foBlockSpaceBefore;
0081: private MinOptMax foBlockSpaceAfter;
0082:
0083: private boolean discardBorderBefore;
0084: private boolean discardBorderAfter;
0085: private boolean discardPaddingBefore;
0086: private boolean discardPaddingAfter;
0087: private MinOptMax effSpaceBefore;
0088: private MinOptMax effSpaceAfter;
0089:
0090: /**
0091: * Create a new block container layout manager.
0092: * @param node block-container node to create the layout manager for.
0093: */
0094: public BlockContainerLayoutManager(BlockContainer node) {
0095: super (node);
0096: }
0097:
0098: /** @see org.apache.fop.layoutmgr.LayoutManager#initialize() */
0099: public void initialize() {
0100: abProps = getBlockContainerFO().getCommonAbsolutePosition();
0101: foBlockSpaceBefore = new SpaceVal(getBlockContainerFO()
0102: .getCommonMarginBlock().spaceBefore, this ).getSpace();
0103: foBlockSpaceAfter = new SpaceVal(getBlockContainerFO()
0104: .getCommonMarginBlock().spaceAfter, this ).getSpace();
0105: startIndent = getBlockContainerFO().getCommonMarginBlock().startIndent
0106: .getValue(this );
0107: endIndent = getBlockContainerFO().getCommonMarginBlock().endIndent
0108: .getValue(this );
0109:
0110: boolean rotated = (getBlockContainerFO()
0111: .getReferenceOrientation() % 180 != 0);
0112: if (rotated) {
0113: height = getBlockContainerFO()
0114: .getInlineProgressionDimension().getOptimum(this )
0115: .getLength();
0116: width = getBlockContainerFO()
0117: .getBlockProgressionDimension().getOptimum(this )
0118: .getLength();
0119: } else {
0120: height = getBlockContainerFO()
0121: .getBlockProgressionDimension().getOptimum(this )
0122: .getLength();
0123: width = getBlockContainerFO()
0124: .getInlineProgressionDimension().getOptimum(this )
0125: .getLength();
0126: }
0127:
0128: bpUnit = 0; //layoutProps.blockProgressionUnit;
0129: if (bpUnit == 0) {
0130: // use optimum space values
0131: adjustedSpaceBefore = getBlockContainerFO()
0132: .getCommonMarginBlock().spaceBefore.getSpace()
0133: .getOptimum(this ).getLength().getValue(this );
0134: adjustedSpaceAfter = getBlockContainerFO()
0135: .getCommonMarginBlock().spaceAfter.getSpace()
0136: .getOptimum(this ).getLength().getValue(this );
0137: } else {
0138: // use minimum space values
0139: adjustedSpaceBefore = getBlockContainerFO()
0140: .getCommonMarginBlock().spaceBefore.getSpace()
0141: .getMinimum(this ).getLength().getValue(this );
0142: adjustedSpaceAfter = getBlockContainerFO()
0143: .getCommonMarginBlock().spaceAfter.getSpace()
0144: .getMinimum(this ).getLength().getValue(this );
0145: }
0146: }
0147:
0148: private void resetSpaces() {
0149: this .discardBorderBefore = false;
0150: this .discardBorderAfter = false;
0151: this .discardPaddingBefore = false;
0152: this .discardPaddingAfter = false;
0153: this .effSpaceBefore = null;
0154: this .effSpaceAfter = null;
0155: }
0156:
0157: /** @return the content IPD */
0158: protected int getRotatedIPD() {
0159: return getBlockContainerFO().getInlineProgressionDimension()
0160: .getOptimum(this ).getLength().getValue(this );
0161: }
0162:
0163: private boolean needClip() {
0164: int overflow = getBlockContainerFO().getOverflow();
0165: return (overflow == EN_HIDDEN || overflow == EN_ERROR_IF_OVERFLOW);
0166: }
0167:
0168: private int getSpaceBefore() {
0169: return foBlockSpaceBefore.opt;
0170: }
0171:
0172: private int getBPIndents() {
0173: int indents = 0;
0174: /* TODO This is wrong isn't it?
0175: indents += getBlockContainerFO().getCommonMarginBlock()
0176: .spaceBefore.getOptimum(this).getLength().getValue(this);
0177: indents += getBlockContainerFO().getCommonMarginBlock()
0178: .spaceAfter.getOptimum(this).getLength().getValue(this);
0179: */
0180: indents += getBlockContainerFO()
0181: .getCommonBorderPaddingBackground()
0182: .getBPPaddingAndBorder(false, this );
0183: return indents;
0184: }
0185:
0186: private boolean isAbsoluteOrFixed() {
0187: return (abProps.absolutePosition == EN_ABSOLUTE)
0188: || (abProps.absolutePosition == EN_FIXED);
0189: }
0190:
0191: private boolean isFixed() {
0192: return (abProps.absolutePosition == EN_FIXED);
0193: }
0194:
0195: /** @see org.apache.fop.layoutmgr.LayoutManager#getContentAreaBPD() */
0196: public int getContentAreaBPD() {
0197: if (autoHeight) {
0198: return -1;
0199: } else {
0200: return this .vpContentBPD;
0201: }
0202: }
0203:
0204: /** @see org.apache.fop.layoutmgr.LayoutManager */
0205: public LinkedList getNextKnuthElements(LayoutContext context,
0206: int alignment) {
0207: resetSpaces();
0208: if (isAbsoluteOrFixed()) {
0209: return getNextKnuthElementsAbsolute(context, alignment);
0210: }
0211:
0212: autoHeight = false;
0213: //boolean rotated = (getBlockContainerFO().getReferenceOrientation() % 180 != 0);
0214: int maxbpd = context.getStackLimit().opt;
0215: int allocBPD;
0216: if (height.getEnum() == EN_AUTO
0217: || (!height.isAbsolute() && getAncestorBlockAreaBPD() <= 0)) {
0218: //auto height when height="auto" or "if that dimension is not specified explicitly
0219: //(i.e., it depends on content's blockprogression-dimension)" (XSL 1.0, 7.14.1)
0220: allocBPD = maxbpd;
0221: autoHeight = true;
0222: } else {
0223: allocBPD = height.getValue(this ); //this is the content-height
0224: allocBPD += getBPIndents();
0225: }
0226: vpContentBPD = allocBPD - getBPIndents();
0227:
0228: referenceIPD = context.getRefIPD();
0229: if (width.getEnum() == EN_AUTO) {
0230: updateContentAreaIPDwithOverconstrainedAdjust();
0231: } else {
0232: int contentWidth = width.getValue(this );
0233: updateContentAreaIPDwithOverconstrainedAdjust(contentWidth);
0234: }
0235:
0236: double contentRectOffsetX = 0;
0237: contentRectOffsetX += getBlockContainerFO()
0238: .getCommonMarginBlock().startIndent.getValue(this );
0239: double contentRectOffsetY = 0;
0240: contentRectOffsetY += getBlockContainerFO()
0241: .getCommonBorderPaddingBackground()
0242: .getBorderBeforeWidth(false);
0243: contentRectOffsetY += getBlockContainerFO()
0244: .getCommonBorderPaddingBackground().getPaddingBefore(
0245: false, this );
0246:
0247: Rectangle2D rect = new Rectangle2D.Double(contentRectOffsetX,
0248: contentRectOffsetY, getContentAreaIPD(),
0249: getContentAreaBPD());
0250: relDims = new FODimension(0, 0);
0251: absoluteCTM = CTM.getCTMandRelDims(getBlockContainerFO()
0252: .getReferenceOrientation(), getBlockContainerFO()
0253: .getWritingMode(), rect, relDims);
0254:
0255: int availableIPD = referenceIPD - getIPIndents();
0256: if (rect.getWidth() > availableIPD) {
0257: log.warn(FONode.decorateWithContextInfo(
0258: "The extent in inline-progression-direction (width) of a block-container is"
0259: + " bigger than the available space ("
0260: + rect.getWidth() + "mpt > "
0261: + context.getRefIPD() + "mpt)",
0262: getBlockContainerFO()));
0263: }
0264:
0265: MinOptMax stackLimit = new MinOptMax(relDims.bpd);
0266:
0267: LinkedList returnedList = null;
0268: LinkedList contentList = new LinkedList();
0269: LinkedList returnList = new LinkedList();
0270:
0271: if (!breakBeforeServed) {
0272: try {
0273: if (addKnuthElementsForBreakBefore(returnList, context)) {
0274: return returnList;
0275: }
0276: } finally {
0277: breakBeforeServed = true;
0278: }
0279: }
0280:
0281: if (!firstVisibleMarkServed) {
0282: addKnuthElementsForSpaceBefore(returnList, alignment);
0283: }
0284:
0285: addKnuthElementsForBorderPaddingBefore(returnList,
0286: !firstVisibleMarkServed);
0287: firstVisibleMarkServed = true;
0288:
0289: if (autoHeight) {
0290: //Spaces, border and padding to be repeated at each break
0291: addPendingMarks(context);
0292:
0293: BlockLevelLayoutManager curLM; // currently active LM
0294: BlockLevelLayoutManager prevLM = null; // previously active LM
0295: while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) {
0296: LayoutContext childLC = new LayoutContext(0);
0297: childLC.copyPendingMarksFrom(context);
0298: // curLM is a ?
0299: childLC.setStackLimit(MinOptMax.subtract(context
0300: .getStackLimit(), stackLimit));
0301: childLC.setRefIPD(relDims.ipd);
0302: childLC.setWritingMode(getBlockContainerFO()
0303: .getWritingMode());
0304:
0305: // get elements from curLM
0306: returnedList = curLM.getNextKnuthElements(childLC,
0307: alignment);
0308: if (returnedList.size() == 1
0309: && ((ListElement) returnedList.getFirst())
0310: .isForcedBreak()) {
0311: // a descendant of this block has break-before
0312: /*
0313: if (returnList.size() == 0) {
0314: // the first child (or its first child ...) has
0315: // break-before;
0316: // all this block, including space before, will be put in
0317: // the
0318: // following page
0319: bSpaceBeforeServed = false;
0320: }*/
0321: contentList.addAll(returnedList);
0322:
0323: // "wrap" the Position inside each element
0324: // moving the elements from contentList to returnList
0325: returnedList = new LinkedList();
0326: wrapPositionElements(contentList, returnList);
0327:
0328: return returnList;
0329: } else {
0330: if (prevLM != null) {
0331: // there is a block handled by prevLM
0332: // before the one handled by curLM
0333: if (mustKeepTogether()
0334: || prevLM.mustKeepWithNext()
0335: || curLM.mustKeepWithPrevious()) {
0336: // add an infinite penalty to forbid a break between
0337: // blocks
0338: contentList.add(new BreakElement(
0339: new Position(this ),
0340: KnuthElement.INFINITE, context));
0341: } else if (!((ListElement) contentList
0342: .getLast()).isGlue()) {
0343: // add a null penalty to allow a break between blocks
0344: contentList.add(new BreakElement(
0345: new Position(this ), 0, context));
0346: } else {
0347: // the last element in contentList is a glue;
0348: // it is a feasible breakpoint, there is no need to add
0349: // a penalty
0350: }
0351: }
0352: contentList.addAll(returnedList);
0353: if (returnedList.size() == 0) {
0354: //Avoid NoSuchElementException below (happens with empty blocks)
0355: continue;
0356: }
0357: if (((ListElement) returnedList.getLast())
0358: .isForcedBreak()) {
0359: // a descendant of this block has break-after
0360: if (curLM.isFinished()) {
0361: // there is no other content in this block;
0362: // it's useless to add space after before a page break
0363: setFinished(true);
0364: }
0365:
0366: returnedList = new LinkedList();
0367: wrapPositionElements(contentList, returnList);
0368:
0369: return returnList;
0370: }
0371: }
0372: prevLM = curLM;
0373: }
0374:
0375: returnedList = new LinkedList();
0376: wrapPositionElements(contentList, returnList);
0377:
0378: } else {
0379: MinOptMax range = new MinOptMax(relDims.ipd);
0380: BlockContainerBreaker breaker = new BlockContainerBreaker(
0381: this , range);
0382: breaker.doLayout(relDims.bpd, autoHeight);
0383: boolean contentOverflows = false;
0384: if (!breaker.isEmpty()) {
0385: contentOverflows = (breaker.deferredAlg.getPageBreaks()
0386: .size() > 1);
0387: }
0388:
0389: Position bcPosition = new BlockContainerPosition(this ,
0390: breaker);
0391: returnList.add(new KnuthBox(vpContentBPD,
0392: notifyPos(bcPosition), false));
0393: //TODO Handle min/opt/max for block-progression-dimension
0394: /* These two elements will be used to add stretchability to the above box
0395: returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE,
0396: false, returnPosition, false));
0397: returnList.add(new KnuthGlue(0, 1 * constantLineHeight, 0,
0398: LINE_NUMBER_ADJUSTMENT, returnPosition, false));
0399: */
0400:
0401: if (contentOverflows) {
0402: log
0403: .warn("Contents overflow block-container viewport: clipping");
0404: if (getBlockContainerFO().getOverflow() == EN_ERROR_IF_OVERFLOW) {
0405: //TODO Throw layout exception
0406: }
0407: }
0408: }
0409: addKnuthElementsForBorderPaddingAfter(returnList, true);
0410: addKnuthElementsForSpaceAfter(returnList, alignment);
0411: addKnuthElementsForBreakAfter(returnList, context);
0412:
0413: setFinished(true);
0414: return returnList;
0415: }
0416:
0417: private LinkedList getNextKnuthElementsAbsolute(
0418: LayoutContext context, int alignment) {
0419: autoHeight = false;
0420:
0421: Point offset = getAbsOffset();
0422: int allocBPD, allocIPD;
0423: if (height.getEnum() == EN_AUTO
0424: || (!height.isAbsolute() && getAncestorBlockAreaBPD() <= 0)) {
0425: //auto height when height="auto" or "if that dimension is not specified explicitly
0426: //(i.e., it depends on content's blockprogression-dimension)" (XSL 1.0, 7.14.1)
0427: allocBPD = 0;
0428: if (abProps.bottom.getEnum() != EN_AUTO) {
0429: int availHeight;
0430: if (isFixed()) {
0431: availHeight = (int) getCurrentPV().getViewArea()
0432: .getHeight();
0433: } else {
0434: availHeight = context.getStackLimit().opt;
0435: }
0436: allocBPD = availHeight;
0437: allocBPD -= offset.y;
0438: if (abProps.bottom.getEnum() != EN_AUTO) {
0439: allocBPD -= abProps.bottom.getValue(this );
0440: if (allocBPD < 0) {
0441: //TODO Fix absolute b-c layout, layout may need to be defferred until
0442: //after page breaking when the size of the containing box is known.
0443: /* Warning disabled due to a interpretation mistake.
0444: * See: http://marc.theaimsgroup.com/?l=fop-dev&m=113189981926163&w=2
0445: log.error("The current combination of top and bottom properties results"
0446: + " in a negative extent for the block-container. 'bottom' may be"
0447: + " at most " + (allocBPD + abProps.bottom.getValue(this)) + " mpt,"
0448: + " but was actually " + abProps.bottom.getValue(this) + " mpt."
0449: + " The nominal available height is " + availHeight + " mpt.");
0450: */
0451: allocBPD = 0;
0452: }
0453: } else {
0454: if (allocBPD < 0) {
0455: /* Warning disabled due to a interpretation mistake.
0456: * See: http://marc.theaimsgroup.com/?l=fop-dev&m=113189981926163&w=2
0457: log.error("The current combination of top and bottom properties results"
0458: + " in a negative extent for the block-container. 'top' may be"
0459: + " at most " + availHeight + " mpt,"
0460: + " but was actually " + offset.y + " mpt."
0461: + " The nominal available height is " + availHeight + " mpt.");
0462: */
0463: allocBPD = 0;
0464: }
0465: }
0466: } else {
0467: int maxbpd = context.getStackLimit().opt;
0468: allocBPD = maxbpd;
0469: autoHeight = true;
0470: }
0471: } else {
0472: allocBPD = height.getValue(this ); //this is the content-height
0473: allocBPD += getBPIndents();
0474: }
0475: if (width.getEnum() == EN_AUTO) {
0476: int availWidth;
0477: if (isFixed()) {
0478: availWidth = (int) getCurrentPV().getViewArea()
0479: .getWidth();
0480: } else {
0481: availWidth = context.getRefIPD();
0482: }
0483: allocIPD = availWidth;
0484: if (abProps.left.getEnum() != EN_AUTO) {
0485: allocIPD -= abProps.left.getValue(this );
0486: }
0487: if (abProps.right.getEnum() != EN_AUTO) {
0488: allocIPD -= abProps.right.getValue(this );
0489: if (allocIPD < 0) {
0490: /* Warning disabled due to a interpretation mistake.
0491: * See: http://marc.theaimsgroup.com/?l=fop-dev&m=113189981926163&w=2
0492: log.error("The current combination of left and right properties results"
0493: + " in a negative extent for the block-container. 'right' may be"
0494: + " at most " + (allocIPD + abProps.right.getValue(this)) + " mpt,"
0495: + " but was actually " + abProps.right.getValue(this) + " mpt."
0496: + " The nominal available width is " + availWidth + " mpt.");
0497: */
0498: allocIPD = 0;
0499: }
0500: } else {
0501: if (allocIPD < 0) {
0502: /* Warning disabled due to a interpretation mistake.
0503: * See: http://marc.theaimsgroup.com/?l=fop-dev&m=113189981926163&w=2
0504: log.error("The current combination of left and right properties results"
0505: + " in a negative extent for the block-container. 'left' may be"
0506: + " at most " + allocIPD + " mpt,"
0507: + " but was actually " + abProps.left.getValue(this) + " mpt."
0508: + " The nominal available width is " + availWidth + " mpt.");
0509: */
0510: allocIPD = 0;
0511: }
0512: }
0513: } else {
0514: allocIPD = width.getValue(this ); //this is the content-width
0515: allocIPD += getIPIndents();
0516: }
0517:
0518: vpContentBPD = allocBPD - getBPIndents();
0519: setContentAreaIPD(allocIPD - getIPIndents());
0520:
0521: double contentRectOffsetX = offset.getX();
0522: contentRectOffsetX += getBlockContainerFO()
0523: .getCommonMarginBlock().startIndent.getValue(this );
0524: double contentRectOffsetY = offset.getY();
0525: contentRectOffsetY += getSpaceBefore(); //TODO Uhm, is that necessary?
0526: contentRectOffsetY += getBlockContainerFO()
0527: .getCommonBorderPaddingBackground()
0528: .getBorderBeforeWidth(false);
0529: contentRectOffsetY += getBlockContainerFO()
0530: .getCommonBorderPaddingBackground().getPaddingBefore(
0531: false, this );
0532:
0533: Rectangle2D rect = new Rectangle2D.Double(contentRectOffsetX,
0534: contentRectOffsetY, getContentAreaIPD(), vpContentBPD);
0535: relDims = new FODimension(0, 0);
0536: absoluteCTM = CTM.getCTMandRelDims(getBlockContainerFO()
0537: .getReferenceOrientation(), getBlockContainerFO()
0538: .getWritingMode(), rect, relDims);
0539:
0540: MinOptMax range = new MinOptMax(relDims.ipd);
0541: BlockContainerBreaker breaker = new BlockContainerBreaker(this ,
0542: range);
0543: breaker.doLayout((autoHeight ? 0 : relDims.bpd), autoHeight);
0544: boolean contentOverflows = breaker.isOverflow();
0545: LinkedList returnList = new LinkedList();
0546: if (!breaker.isEmpty()) {
0547: Position bcPosition = new BlockContainerPosition(this ,
0548: breaker);
0549: returnList
0550: .add(new KnuthBox(0, notifyPos(bcPosition), false));
0551:
0552: //TODO Maybe check for page overflow when autoHeight=true
0553: if (!autoHeight
0554: & (contentOverflows/*usedBPD > relDims.bpd*/)) {
0555: log
0556: .warn("Contents overflow block-container viewport: clipping");
0557: if (getBlockContainerFO().getOverflow() == EN_ERROR_IF_OVERFLOW) {
0558: //TODO Throw layout exception
0559: }
0560: }
0561: }
0562:
0563: setFinished(true);
0564: return returnList;
0565: }
0566:
0567: private class BlockContainerPosition extends NonLeafPosition {
0568:
0569: private BlockContainerBreaker breaker;
0570:
0571: public BlockContainerPosition(LayoutManager lm,
0572: BlockContainerBreaker breaker) {
0573: super (lm, null);
0574: this .breaker = breaker;
0575: }
0576:
0577: public BlockContainerBreaker getBreaker() {
0578: return this .breaker;
0579: }
0580:
0581: }
0582:
0583: private class BlockContainerBreaker extends AbstractBreaker {
0584:
0585: private BlockContainerLayoutManager bclm;
0586: private MinOptMax ipd;
0587:
0588: //Info for deferred adding of areas
0589: private PageBreakingAlgorithm deferredAlg;
0590: private BlockSequence deferredOriginalList;
0591: private BlockSequence deferredEffectiveList;
0592:
0593: public BlockContainerBreaker(BlockContainerLayoutManager bclm,
0594: MinOptMax ipd) {
0595: this .bclm = bclm;
0596: this .ipd = ipd;
0597: }
0598:
0599: /** @see org.apache.fop.layoutmgr.AbstractBreaker#observeElementList(java.util.List) */
0600: protected void observeElementList(List elementList) {
0601: ElementListObserver.observe(elementList, "block-container",
0602: bclm.getBlockContainerFO().getId());
0603: }
0604:
0605: /** @see org.apache.fop.layoutmgr.AbstractBreaker#isPartOverflowRecoveryActivated() */
0606: protected boolean isPartOverflowRecoveryActivated() {
0607: //For block-containers, this must be disabled because of wanted overflow.
0608: return false;
0609: }
0610:
0611: /** @see org.apache.fop.layoutmgr.AbstractBreaker#isSinglePartFavored() */
0612: protected boolean isSinglePartFavored() {
0613: return true;
0614: }
0615:
0616: public int getDifferenceOfFirstPart() {
0617: PageBreakPosition pbp = (PageBreakPosition) this .deferredAlg
0618: .getPageBreaks().getFirst();
0619: return pbp.difference;
0620: }
0621:
0622: public boolean isOverflow() {
0623: if (isEmpty()) {
0624: return false;
0625: } else {
0626: return (deferredAlg.getPageBreaks().size() > 1);
0627: }
0628: }
0629:
0630: protected LayoutManager getTopLevelLM() {
0631: return bclm;
0632: }
0633:
0634: protected LayoutContext createLayoutContext() {
0635: LayoutContext lc = super .createLayoutContext();
0636: lc.setRefIPD(ipd.opt);
0637: lc.setWritingMode(getBlockContainerFO().getWritingMode());
0638: return lc;
0639: }
0640:
0641: protected LinkedList getNextKnuthElements(
0642: LayoutContext context, int alignment) {
0643: LayoutManager curLM; // currently active LM
0644: LinkedList returnList = new LinkedList();
0645:
0646: while ((curLM = getChildLM()) != null) {
0647: LayoutContext childLC = new LayoutContext(0);
0648: childLC.setStackLimit(context.getStackLimit());
0649: childLC.setRefIPD(context.getRefIPD());
0650: childLC.setWritingMode(getBlockContainerFO()
0651: .getWritingMode());
0652:
0653: LinkedList returnedList = null;
0654: if (!curLM.isFinished()) {
0655: returnedList = curLM.getNextKnuthElements(childLC,
0656: alignment);
0657: }
0658: if (returnedList != null) {
0659: bclm.wrapPositionElements(returnedList, returnList);
0660: }
0661: }
0662: SpaceResolver.resolveElementList(returnList);
0663: setFinished(true);
0664: return returnList;
0665: }
0666:
0667: protected int getCurrentDisplayAlign() {
0668: return getBlockContainerFO().getDisplayAlign();
0669: }
0670:
0671: protected boolean hasMoreContent() {
0672: return !isFinished();
0673: }
0674:
0675: protected void addAreas(PositionIterator posIter,
0676: LayoutContext context) {
0677: AreaAdditionUtil.addAreas(bclm, posIter, context);
0678: }
0679:
0680: protected void doPhase3(PageBreakingAlgorithm alg,
0681: int partCount, BlockSequence originalList,
0682: BlockSequence effectiveList) {
0683: //Defer adding of areas until addAreas is called by the parent LM
0684: this .deferredAlg = alg;
0685: this .deferredOriginalList = originalList;
0686: this .deferredEffectiveList = effectiveList;
0687: }
0688:
0689: protected void finishPart(PageBreakingAlgorithm alg,
0690: PageBreakPosition pbp) {
0691: //nop for bclm
0692: }
0693:
0694: protected LayoutManager getCurrentChildLM() {
0695: return curChildLM;
0696: }
0697:
0698: public void addContainedAreas() {
0699: if (isEmpty()) {
0700: return;
0701: }
0702: //Rendering all parts (not just the first) at once for the case where the parts that
0703: //overflow should be visible.
0704: //TODO Check if this has any unwanted side-effects. Feels a bit like a hack.
0705: this .addAreas(this .deferredAlg,
0706: /*1*/this .deferredAlg.getPageBreaks().size(),
0707: this .deferredOriginalList,
0708: this .deferredEffectiveList);
0709: }
0710:
0711: }
0712:
0713: private Point getAbsOffset() {
0714: int x = 0;
0715: int y = 0;
0716: if (abProps.left.getEnum() != EN_AUTO) {
0717: x = abProps.left.getValue(this );
0718: }
0719: if (abProps.top.getEnum() != EN_AUTO) {
0720: y = abProps.top.getValue(this );
0721: }
0722: return new Point(x, y);
0723: }
0724:
0725: /** @see org.apache.fop.layoutmgr.LayoutManager */
0726: public void addAreas(PositionIterator parentIter,
0727: LayoutContext layoutContext) {
0728: getParentArea(null);
0729:
0730: // if this will create the first block area in a page
0731: // and display-align is bottom or center, add space before
0732: if (layoutContext.getSpaceBefore() > 0) {
0733: addBlockSpacing(0.0, new MinOptMax(layoutContext
0734: .getSpaceBefore()));
0735: }
0736:
0737: LayoutManager childLM = null;
0738: LayoutManager lastLM = null;
0739: LayoutContext lc = new LayoutContext(0);
0740: lc.setSpaceAdjust(layoutContext.getSpaceAdjust());
0741: // set space after in the LayoutContext for children
0742: if (layoutContext.getSpaceAfter() > 0) {
0743: lc.setSpaceAfter(layoutContext.getSpaceAfter());
0744: }
0745: BlockContainerPosition bcpos = null;
0746: PositionIterator childPosIter;
0747:
0748: // "unwrap" the NonLeafPositions stored in parentIter
0749: // and put them in a new list;
0750: LinkedList positionList = new LinkedList();
0751: Position pos;
0752: boolean bSpaceBefore = false;
0753: boolean bSpaceAfter = false;
0754: Position firstPos = null;
0755: Position lastPos = null;
0756: while (parentIter.hasNext()) {
0757: pos = (Position) parentIter.next();
0758: if (pos.getIndex() >= 0) {
0759: if (firstPos == null) {
0760: firstPos = pos;
0761: }
0762: lastPos = pos;
0763: }
0764: Position innerPosition = pos;
0765: if (pos instanceof NonLeafPosition) {
0766: innerPosition = ((NonLeafPosition) pos).getPosition();
0767: }
0768: if (pos instanceof BlockContainerPosition) {
0769: if (bcpos != null) {
0770: throw new IllegalStateException(
0771: "Only one BlockContainerPosition allowed");
0772: }
0773: bcpos = (BlockContainerPosition) pos;
0774: //Add child areas inside the reference area
0775: //bcpos.getBreaker().addContainedAreas();
0776: } else if (innerPosition == null) {
0777: if (pos instanceof NonLeafPosition) {
0778: // pos was created by this BCLM and was inside an element
0779: // representing space before or after
0780: // this means the space was not discarded
0781: if (positionList.size() == 0 && bcpos == null) {
0782: // pos was in the element representing space-before
0783: bSpaceBefore = true;
0784: } else {
0785: // pos was in the element representing space-after
0786: bSpaceAfter = true;
0787: }
0788: } else {
0789: //ignore (probably a Position for a simple penalty between blocks)
0790: }
0791: } else if (innerPosition.getLM() == this
0792: && !(innerPosition instanceof MappingPosition)) {
0793: // pos was created by this BlockLM and was inside a penalty
0794: // allowing or forbidding a page break
0795: // nothing to do
0796: } else {
0797: // innerPosition was created by another LM
0798: positionList.add(innerPosition);
0799: lastLM = innerPosition.getLM();
0800: }
0801: }
0802:
0803: getPSLM().addIDToPage(getBlockContainerFO().getId());
0804: if (markers != null) {
0805: getCurrentPV().addMarkers(markers, true, isFirst(firstPos),
0806: isLast(lastPos));
0807: }
0808:
0809: if (bcpos == null) {
0810: if (bpUnit == 0) {
0811: // the Positions in positionList were inside the elements
0812: // created by the LineLM
0813: childPosIter = new StackingIter(positionList
0814: .listIterator());
0815: } else {
0816: // the Positions in positionList were inside the elements
0817: // created by the BCLM in the createUnitElements() method
0818: //if (((Position) positionList.getLast()) instanceof
0819: // LeafPosition) {
0820: // // the last item inside positionList is a LeafPosition
0821: // // (a LineBreakPosition, more precisely); this means that
0822: // // the whole paragraph is on the same page
0823: // childPosIter = new KnuthPossPosIter(storedList, 0,
0824: // storedList.size());
0825: //} else {
0826: // // the last item inside positionList is a Position;
0827: // // this means that the paragraph has been split
0828: // // between consecutive pages
0829: LinkedList splitList = new LinkedList();
0830: int splitLength = 0;
0831: int iFirst = ((MappingPosition) positionList.getFirst())
0832: .getFirstIndex();
0833: int iLast = ((MappingPosition) positionList.getLast())
0834: .getLastIndex();
0835: // copy from storedList to splitList all the elements from
0836: // iFirst to iLast
0837: ListIterator storedListIterator = storedList
0838: .listIterator(iFirst);
0839: while (storedListIterator.nextIndex() <= iLast) {
0840: KnuthElement element = (KnuthElement) storedListIterator
0841: .next();
0842: // some elements in storedList (i.e. penalty items) were created
0843: // by this BlockLM, and must be ignored
0844: if (element.getLayoutManager() != this ) {
0845: splitList.add(element);
0846: splitLength += element.getW();
0847: lastLM = element.getLayoutManager();
0848: }
0849: }
0850: //log.debug("Adding areas from " + iFirst + " to " + iLast);
0851: //log.debug("splitLength= " + splitLength
0852: // + " (" + neededUnits(splitLength) + " units') "
0853: // + (neededUnits(splitLength) * bpUnit - splitLength)
0854: // + " spacing");
0855: // add space before and / or after the paragraph
0856: // to reach a multiple of bpUnit
0857: if (bSpaceBefore && bSpaceAfter) {
0858: foBlockSpaceBefore = new SpaceVal(
0859: getBlockContainerFO()
0860: .getCommonMarginBlock().spaceBefore,
0861: this ).getSpace();
0862: foBlockSpaceAfter = new SpaceVal(
0863: getBlockContainerFO()
0864: .getCommonMarginBlock().spaceAfter,
0865: this ).getSpace();
0866: adjustedSpaceBefore = (neededUnits(splitLength
0867: + foBlockSpaceBefore.min
0868: + foBlockSpaceAfter.min)
0869: * bpUnit - splitLength) / 2;
0870: adjustedSpaceAfter = neededUnits(splitLength
0871: + foBlockSpaceBefore.min
0872: + foBlockSpaceAfter.min)
0873: * bpUnit
0874: - splitLength
0875: - adjustedSpaceBefore;
0876: } else if (bSpaceBefore) {
0877: adjustedSpaceBefore = neededUnits(splitLength
0878: + foBlockSpaceBefore.min)
0879: * bpUnit - splitLength;
0880: } else {
0881: adjustedSpaceAfter = neededUnits(splitLength
0882: + foBlockSpaceAfter.min)
0883: * bpUnit - splitLength;
0884: }
0885: //log.debug("space before = " + adjustedSpaceBefore
0886: // + " space after = " + adjustedSpaceAfter + " total = " +
0887: // (adjustedSpaceBefore + adjustedSpaceAfter + splitLength));
0888: childPosIter = new KnuthPossPosIter(splitList, 0,
0889: splitList.size());
0890: //}
0891: }
0892:
0893: while ((childLM = childPosIter.getNextChildLM()) != null) {
0894: // set last area flag
0895: lc.setFlags(LayoutContext.LAST_AREA, (layoutContext
0896: .isLastArea() && childLM == lastLM));
0897: /*LF*/lc.setStackLimit(layoutContext.getStackLimit());
0898: // Add the line areas to Area
0899: childLM.addAreas(childPosIter, lc);
0900: }
0901: } else {
0902: //Add child areas inside the reference area
0903: bcpos.getBreaker().addContainedAreas();
0904: }
0905:
0906: if (markers != null) {
0907: getCurrentPV().addMarkers(markers, false,
0908: isFirst(firstPos), isLast(lastPos));
0909: }
0910:
0911: TraitSetter.addSpaceBeforeAfter(viewportBlockArea,
0912: layoutContext.getSpaceAdjust(), effSpaceBefore,
0913: effSpaceAfter);
0914: flush();
0915:
0916: viewportBlockArea = null;
0917: referenceArea = null;
0918: resetSpaces();
0919:
0920: getPSLM().notifyEndOfLayout(
0921: ((BlockContainer) getFObj()).getId());
0922: }
0923:
0924: /**
0925: * Get the parent area for children of this block container.
0926: * This returns the current block container area
0927: * and creates it if required.
0928: *
0929: * @see org.apache.fop.layoutmgr.LayoutManager#getParentArea(Area)
0930: */
0931: public Area getParentArea(Area childArea) {
0932: if (referenceArea == null) {
0933: viewportBlockArea = new BlockViewport();
0934: viewportBlockArea.addTrait(Trait.IS_VIEWPORT_AREA,
0935: Boolean.TRUE);
0936: viewportBlockArea.setIPD(getContentAreaIPD());
0937: if (autoHeight) {
0938: viewportBlockArea.setBPD(0);
0939: } else {
0940: viewportBlockArea.setBPD(getContentAreaBPD());
0941: }
0942:
0943: TraitSetter.setProducerID(viewportBlockArea,
0944: getBlockContainerFO().getId());
0945: TraitSetter.addBorders(viewportBlockArea,
0946: getBlockContainerFO()
0947: .getCommonBorderPaddingBackground(),
0948: discardBorderBefore, discardBorderAfter, false,
0949: false, this );
0950: TraitSetter.addPadding(viewportBlockArea,
0951: getBlockContainerFO()
0952: .getCommonBorderPaddingBackground(),
0953: discardPaddingBefore, discardPaddingAfter, false,
0954: false, this );
0955: // TraitSetter.addBackground(viewportBlockArea,
0956: // getBlockContainerFO().getCommonBorderPaddingBackground(),
0957: // this);
0958: TraitSetter.addMargins(viewportBlockArea,
0959: getBlockContainerFO()
0960: .getCommonBorderPaddingBackground(),
0961: startIndent, endIndent, this );
0962:
0963: viewportBlockArea.setCTM(absoluteCTM);
0964: viewportBlockArea.setClip(needClip());
0965: /*
0966: if (getSpaceBefore() != 0) {
0967: viewportBlockArea.addTrait(Trait.SPACE_BEFORE, new Integer(getSpaceBefore()));
0968: }
0969: if (foBlockSpaceAfter.opt != 0) {
0970: viewportBlockArea.addTrait(Trait.SPACE_AFTER, new Integer(foBlockSpaceAfter.opt));
0971: }*/
0972:
0973: if (abProps.absolutePosition == EN_ABSOLUTE
0974: || abProps.absolutePosition == EN_FIXED) {
0975: Point offset = getAbsOffset();
0976: viewportBlockArea.setXOffset(offset.x);
0977: viewportBlockArea.setYOffset(offset.y);
0978: } else {
0979: //nop
0980: }
0981:
0982: referenceArea = new Block();
0983: referenceArea.addTrait(Trait.IS_REFERENCE_AREA,
0984: Boolean.TRUE);
0985: TraitSetter.setProducerID(referenceArea,
0986: getBlockContainerFO().getId());
0987:
0988: if (abProps.absolutePosition == EN_ABSOLUTE) {
0989: viewportBlockArea.setPositioning(Block.ABSOLUTE);
0990: } else if (abProps.absolutePosition == EN_FIXED) {
0991: viewportBlockArea.setPositioning(Block.FIXED);
0992: }
0993:
0994: // Set up dimensions
0995: // Must get dimensions from parent area
0996: /*Area parentArea =*/parentLM.getParentArea(referenceArea);
0997: //int referenceIPD = parentArea.getIPD();
0998: referenceArea.setIPD(relDims.ipd);
0999: // Get reference IPD from parentArea
1000: setCurrentArea(viewportBlockArea); // ??? for generic operations
1001: }
1002: return referenceArea;
1003: }
1004:
1005: /**
1006: * Add the child to the block container.
1007: *
1008: * @see org.apache.fop.layoutmgr.LayoutManager#addChildArea(Area)
1009: */
1010: public void addChildArea(Area childArea) {
1011: if (referenceArea != null) {
1012: referenceArea.addBlock((Block) childArea);
1013: }
1014: }
1015:
1016: /**
1017: * @see org.apache.fop.layoutmgr.LayoutManager#resetPosition(org.apache.fop.layoutmgr.Position)
1018: */
1019: public void resetPosition(Position resetPos) {
1020: if (resetPos == null) {
1021: reset(null);
1022: }
1023: }
1024:
1025: /**
1026: * Force current area to be added to parent area.
1027: * @see org.apache.fop.layoutmgr.BlockStackingLayoutManager#flush()
1028: */
1029: protected void flush() {
1030: viewportBlockArea.addBlock(referenceArea, autoHeight);
1031:
1032: TraitSetter.addBackground(viewportBlockArea,
1033: getBlockContainerFO()
1034: .getCommonBorderPaddingBackground(), this );
1035:
1036: // Fake a 0 height for absolute positioned blocks.
1037: int saveBPD = viewportBlockArea.getBPD();
1038: if (viewportBlockArea.getPositioning() == Block.ABSOLUTE) {
1039: viewportBlockArea.setBPD(0);
1040: }
1041: super .flush();
1042: // Restore the right height.
1043: if (viewportBlockArea.getPositioning() == Block.ABSOLUTE) {
1044: viewportBlockArea.setBPD(saveBPD);
1045: }
1046: }
1047:
1048: /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager */
1049: public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
1050: // TODO Auto-generated method stub
1051: return 0;
1052: }
1053:
1054: /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager */
1055: public void discardSpace(KnuthGlue spaceGlue) {
1056: // TODO Auto-generated method stub
1057:
1058: }
1059:
1060: /**
1061: * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether()
1062: */
1063: public boolean mustKeepTogether() {
1064: //TODO Keeps will have to be more sophisticated sooner or later
1065: return (!getBlockContainerFO().getKeepTogether()
1066: .getWithinPage().isAuto()
1067: || !getBlockContainerFO().getKeepTogether()
1068: .getWithinColumn().isAuto()
1069: || (getParent() instanceof BlockLevelLayoutManager && ((BlockLevelLayoutManager) getParent())
1070: .mustKeepTogether()) || (getParent() instanceof InlineLayoutManager && ((InlineLayoutManager) getParent())
1071: .mustKeepTogether()));
1072: }
1073:
1074: /**
1075: * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious()
1076: */
1077: public boolean mustKeepWithPrevious() {
1078: return !getBlockContainerFO().getKeepWithPrevious()
1079: .getWithinPage().isAuto()
1080: || !getBlockContainerFO().getKeepWithPrevious()
1081: .getWithinColumn().isAuto();
1082: }
1083:
1084: /**
1085: * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext()
1086: */
1087: public boolean mustKeepWithNext() {
1088: return !getBlockContainerFO().getKeepWithNext().getWithinPage()
1089: .isAuto()
1090: || !getBlockContainerFO().getKeepWithNext()
1091: .getWithinColumn().isAuto();
1092: }
1093:
1094: /**
1095: * @return the BlockContainer node
1096: */
1097: protected BlockContainer getBlockContainerFO() {
1098: return (BlockContainer) fobj;
1099: }
1100:
1101: // --------- Property Resolution related functions --------- //
1102:
1103: /**
1104: * @see org.apache.fop.layoutmgr.LayoutManager#getGeneratesReferenceArea
1105: */
1106: public boolean getGeneratesReferenceArea() {
1107: return true;
1108: }
1109:
1110: /**
1111: * @see org.apache.fop.layoutmgr.LayoutManager#getGeneratesBlockArea
1112: */
1113: public boolean getGeneratesBlockArea() {
1114: return true;
1115: }
1116:
1117: /** @see org.apache.fop.layoutmgr.ConditionalElementListener */
1118: public void notifySpace(RelSide side, MinOptMax effectiveLength) {
1119: if (RelSide.BEFORE == side) {
1120: if (log.isDebugEnabled()) {
1121: log
1122: .debug(this + ": Space " + side + ", "
1123: + this .effSpaceBefore + "-> "
1124: + effectiveLength);
1125: }
1126: this .effSpaceBefore = effectiveLength;
1127: } else {
1128: if (log.isDebugEnabled()) {
1129: log.debug(this + ": Space " + side + ", "
1130: + this .effSpaceAfter + "-> " + effectiveLength);
1131: }
1132: this .effSpaceAfter = effectiveLength;
1133: }
1134: }
1135:
1136: /** @see org.apache.fop.layoutmgr.ConditionalElementListener */
1137: public void notifyBorder(RelSide side, MinOptMax effectiveLength) {
1138: if (effectiveLength == null) {
1139: if (RelSide.BEFORE == side) {
1140: this .discardBorderBefore = true;
1141: } else {
1142: this .discardBorderAfter = true;
1143: }
1144: }
1145: if (log.isDebugEnabled()) {
1146: log.debug(this + ": Border " + side + " -> "
1147: + effectiveLength);
1148: }
1149: }
1150:
1151: /** @see org.apache.fop.layoutmgr.ConditionalElementListener */
1152: public void notifyPadding(RelSide side, MinOptMax effectiveLength) {
1153: if (effectiveLength == null) {
1154: if (RelSide.BEFORE == side) {
1155: this .discardPaddingBefore = true;
1156: } else {
1157: this .discardPaddingAfter = true;
1158: }
1159: }
1160: if (log.isDebugEnabled()) {
1161: log.debug(this + ": Padding " + side + " -> "
1162: + effectiveLength);
1163: }
1164: }
1165:
1166: }
|