blob: 1fda2cfc483137209c588fd744f7e95e36950315 [file] [log] [blame]
/*
* Copyright 1999-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* $Id$ */
package org.apache.fop.layoutmgr;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.awt.Point;
import java.awt.geom.Rectangle2D;
import org.apache.fop.area.Area;
import org.apache.fop.area.BlockViewport;
import org.apache.fop.area.Block;
import org.apache.fop.area.Trait;
import org.apache.fop.fo.flow.BlockContainer;
import org.apache.fop.fo.properties.CommonAbsolutePosition;
import org.apache.fop.area.CTM;
import org.apache.fop.datatypes.FODimension;
import org.apache.fop.datatypes.Length;
import org.apache.fop.datatypes.PercentBase;
import org.apache.fop.traits.MinOptMax;
import org.apache.fop.traits.SpaceVal;
/**
* LayoutManager for a block-container FO.
*/
public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
private BlockViewport viewportBlockArea;
private Block referenceArea;
private List childBreaks = new java.util.ArrayList();
private CommonAbsolutePosition abProps;
private FODimension relDims;
private CTM absoluteCTM;
private boolean clip = false;
private Length width;
private Length height;
private int vpContentIPD;
private int vpContentBPD;
private int usedBPD;
// When viewport should grow with the content.
private boolean autoHeight = true;
private int referenceIPD;
/* holds the (one-time use) fo:block space-before
and -after properties. Large fo:blocks are split
into multiple Area.Blocks to accomodate the subsequent
regions (pages) they are placed on. space-before
is applied at the beginning of the first
Block and space-after at the end of the last Block
used in rendering the fo:block.
*/
//TODO space-before|after: handle space-resolution rules
private MinOptMax foBlockSpaceBefore;
private MinOptMax foBlockSpaceAfter;
private boolean bBreakBeforeServed = false;
private boolean bSpaceBeforeServed = false;
/*LF*/
/** Only used to store the original list when createUnitElements is called */
//TODO Maybe pull up as protected member if also used in this class (JM)
private LinkedList storedList = null;
/**
* Create a new block container layout manager.
* @param node block-container node to create the layout manager for.
*/
public BlockContainerLayoutManager(BlockContainer node) {
super(node);
}
/**
* @see org.apache.fop.layoutmgr.AbstractLayoutManager#initProperties()
*/
protected void initProperties() {
abProps = getBlockContainerFO().getCommonAbsolutePosition();
foBlockSpaceBefore = new SpaceVal(getBlockContainerFO().getCommonMarginBlock().spaceBefore).getSpace();
foBlockSpaceAfter = new SpaceVal(getBlockContainerFO().getCommonMarginBlock().spaceAfter).getSpace();
boolean rotated = (getBlockContainerFO().getReferenceOrientation() % 180 != 0);
if (rotated) {
height = getBlockContainerFO().getInlineProgressionDimension().getOptimum().getLength();
width = getBlockContainerFO().getBlockProgressionDimension().getOptimum().getLength();
} else {
height = getBlockContainerFO().getBlockProgressionDimension().getOptimum().getLength();
width = getBlockContainerFO().getInlineProgressionDimension().getOptimum().getLength();
}
/*LF*/ bpUnit = 0; //layoutProps.blockProgressionUnit;
/*LF*/ if (bpUnit == 0) {
/*LF*/ // use optimum space values
/*LF*/ adjustedSpaceBefore = getBlockContainerFO().getCommonMarginBlock().spaceBefore.getSpace().getOptimum().getLength().getValue();
/*LF*/ adjustedSpaceAfter = getBlockContainerFO().getCommonMarginBlock().spaceAfter.getSpace().getOptimum().getLength().getValue();
/*LF*/ } else {
/*LF*/ // use minimum space values
/*LF*/ adjustedSpaceBefore = getBlockContainerFO().getCommonMarginBlock().spaceBefore.getSpace().getMinimum().getLength().getValue();
/*LF*/ adjustedSpaceAfter = getBlockContainerFO().getCommonMarginBlock().spaceAfter.getSpace().getMinimum().getLength().getValue();
/*LF*/ }
}
/** @return the content IPD */
protected int getRotatedIPD() {
return getBlockContainerFO().getInlineProgressionDimension().getOptimum().getLength().getValue();
}
private int getSpaceBefore() {
return foBlockSpaceBefore.opt;
}
private int getBPIndents() {
int indents = 0;
indents += getBlockContainerFO().getCommonMarginBlock().spaceBefore.getOptimum().getLength().getValue();
indents += getBlockContainerFO().getCommonMarginBlock().spaceAfter.getOptimum().getLength().getValue();
indents += getBlockContainerFO().getCommonBorderPaddingBackground().getBPPaddingAndBorder(false);
return indents;
}
private int getIPIndents() {
int iIndents = 0;
iIndents += getBlockContainerFO().getCommonMarginBlock().startIndent.getValue();
iIndents += getBlockContainerFO().getCommonMarginBlock().endIndent.getValue();
return iIndents;
}
private boolean isAbsoluteOrFixed() {
return (abProps.absolutePosition == EN_ABSOLUTE)
|| (abProps.absolutePosition == EN_FIXED);
}
private boolean isFixed() {
return (abProps.absolutePosition == EN_FIXED);
}
/**
* @see org.apache.fop.layoutmgr.LayoutManager#getNextKnuthElements(org.apache.fop.layoutmgr.LayoutContext, int)
*/
public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
if (isAbsoluteOrFixed()) {
return getNextKnuthElementsAbsolute(context, alignment);
}
autoHeight = false;
boolean rotated = (getBlockContainerFO().getReferenceOrientation() % 180 != 0); //vals[0] == 0.0;
referenceIPD = context.getRefIPD();
int maxbpd = context.getStackLimit().opt;
int allocBPD, allocIPD;
if (height.getEnum() != EN_AUTO) {
allocBPD = height.getValue(); //this is the content-height
allocBPD += getBPIndents();
} else {
allocBPD = maxbpd;
autoHeight = true;
}
if (width.getEnum() != EN_AUTO) {
allocIPD = width.getValue(); //this is the content-width
allocIPD += getIPIndents();
} else {
allocIPD = referenceIPD;
}
vpContentBPD = allocBPD - getBPIndents();
vpContentIPD = allocIPD - getIPIndents();
double contentRectOffsetX = 0;
contentRectOffsetX += getBlockContainerFO().getCommonMarginBlock().startIndent.getValue();
double contentRectOffsetY = 0;
//contentRectOffsetY += getBlockContainerFO().getCommonMarginBlock().startIndent.getValue();
//contentRectOffsetY += getSpaceBefore();
contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getPaddingBefore(false);
Rectangle2D rect = new Rectangle2D.Double(
contentRectOffsetX, contentRectOffsetY,
vpContentIPD, vpContentBPD);
relDims = new FODimension(0, 0);
absoluteCTM = CTM.getCTMandRelDims(getBlockContainerFO().getReferenceOrientation(),
getBlockContainerFO().getWritingMode(), rect, relDims);
MinOptMax stackLimit = new MinOptMax(relDims.bpd);
LinkedList returnedList = null;
LinkedList contentList = new LinkedList();
LinkedList returnList = new LinkedList();
Position returnPosition = new NonLeafPosition(this, null);
if (!bBreakBeforeServed) {
try {
if (addKnuthElementsForBreakBefore(returnList, returnPosition)) {
return returnList;
}
} finally {
bBreakBeforeServed = true;
}
}
if (!bSpaceBeforeServed) {
addKnuthElementsForSpaceBefore(returnList, returnPosition, alignment);
bSpaceBeforeServed = true;
}
addKnuthElementsForBorderPaddingBefore(returnList, returnPosition);
if (autoHeight) {
BlockLevelLayoutManager curLM; // currently active LM
BlockLevelLayoutManager prevLM = null; // previously active LM
while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) {
LayoutContext childLC = new LayoutContext(0);
// curLM is a ?
childLC.setStackLimit(MinOptMax.subtract(context
.getStackLimit(), stackLimit));
childLC.setRefIPD(relDims.ipd);
// get elements from curLM
returnedList = curLM.getNextKnuthElements(childLC, alignment);
if (returnedList.size() == 1
&& ((KnuthElement) returnedList.getFirst()).isPenalty()
&& ((KnuthPenalty) returnedList.getFirst()).getP() == -KnuthElement.INFINITE) {
// a descendant of this block has break-before
if (returnList.size() == 0) {
// the first child (or its first child ...) has
// break-before;
// all this block, including space before, will be put in
// the
// following page
bSpaceBeforeServed = false;
}
contentList.addAll(returnedList);
// "wrap" the Position inside each element
// moving the elements from contentList to returnList
returnedList = new LinkedList();
wrapPositionElements(contentList, returnList);
return returnList;
} else {
if (prevLM != null) {
// there is a block handled by prevLM
// before the one handled by curLM
if (mustKeepTogether()
|| prevLM.mustKeepWithNext()
|| curLM.mustKeepWithPrevious()) {
// add an infinite penalty to forbid a break between
// blocks
contentList.add(new KnuthPenalty(0,
KnuthElement.INFINITE, false,
new Position(this), false));
} else if (!((KnuthElement) contentList.getLast()).isGlue()) {
// add a null penalty to allow a break between blocks
contentList.add(new KnuthPenalty(0, 0, false,
new Position(this), false));
} else {
// the last element in contentList is a glue;
// it is a feasible breakpoint, there is no need to add
// a penalty
}
}
contentList.addAll(returnedList);
if (returnedList.size() == 0) {
//Avoid NoSuchElementException below (happens with empty blocks)
continue;
}
if (((KnuthElement) returnedList.getLast()).isPenalty()
&& ((KnuthPenalty) returnedList.getLast()).getP() == -KnuthElement.INFINITE) {
// a descendant of this block has break-after
if (curLM.isFinished()) {
// there is no other content in this block;
// it's useless to add space after before a page break
setFinished(true);
}
returnedList = new LinkedList();
wrapPositionElements(contentList, returnList);
return returnList;
}
}
prevLM = curLM;
}
returnedList = new LinkedList();
wrapPositionElements(contentList, returnList);
} else {
MinOptMax range = new MinOptMax(relDims.ipd);
BlockContainerBreaker breaker = new BlockContainerBreaker(this, range);
breaker.doLayout(relDims.bpd);
boolean contentOverflows = false;
if (!breaker.isEmpty()) {
contentOverflows = (breaker.deferredAlg.getPageBreaks().size() > 1);
}
Position bcPosition = new BlockContainerPosition(this, breaker);
returnList.add(new KnuthBox(vpContentBPD, bcPosition, false));
//TODO Handle min/opt/max for block-progression-dimension
/* These two elements will be used to add stretchability to the above box
returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, returnPosition, false));
returnList.add(new KnuthGlue(0, 1 * constantLineHeight, 0,
LINE_NUMBER_ADJUSTMENT, returnPosition, false));
*/
if (contentOverflows) {
log.warn("Contents overflow block-container viewport: clipping");
if (getBlockContainerFO().getOverflow() == EN_HIDDEN) {
clip = true;
} else if (getBlockContainerFO().getOverflow() == EN_ERROR_IF_OVERFLOW) {
//TODO Throw layout exception
clip = true;
}
}
}
addKnuthElementsForBorderPaddingAfter(returnList, returnPosition);
addKnuthElementsForSpaceAfter(returnList, returnPosition, alignment);
addKnuthElementsForBreakAfter(returnList, returnPosition);
setFinished(true);
return returnList;
}
private LinkedList getNextKnuthElementsAbsolute(LayoutContext context, int alignment) {
MinOptMax stackSize = new MinOptMax();
autoHeight = false;
Point offset = getAbsOffset();
int allocBPD, allocIPD;
if (height.getEnum() != EN_AUTO) {
allocBPD = height.getValue(); //this is the content-height
allocBPD += getBPIndents();
} else {
allocBPD = 0;
if (abProps.bottom.getEnum() != EN_AUTO) {
if (isFixed()) {
allocBPD = (int)getCurrentPV().getViewArea().getHeight();
} else {
allocBPD = context.getStackLimit().opt;
}
allocBPD -= offset.y;
if (abProps.bottom.getEnum() != EN_AUTO) {
allocBPD -= abProps.bottom.getValue();
}
} else {
autoHeight = true;
}
}
if (width.getEnum() != EN_AUTO) {
allocIPD = width.getValue(); //this is the content-width
allocIPD += getIPIndents();
} else {
if (isFixed()) {
allocIPD = (int)getCurrentPV().getViewArea().getWidth();
} else {
allocIPD = context.getRefIPD();
}
if (abProps.left.getEnum() != EN_AUTO) {
allocIPD -= abProps.left.getValue();
}
if (abProps.right.getEnum() != EN_AUTO) {
allocIPD -= abProps.right.getValue();
}
}
vpContentBPD = allocBPD - getBPIndents();
vpContentIPD = allocIPD - getIPIndents();
double contentRectOffsetX = offset.getX();
contentRectOffsetX += getBlockContainerFO().getCommonMarginBlock().startIndent.getValue();
double contentRectOffsetY = offset.getY();
contentRectOffsetY += getSpaceBefore();
contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getPaddingBefore(false);
Rectangle2D rect = new Rectangle2D.Double(
contentRectOffsetX, contentRectOffsetY,
vpContentIPD, vpContentBPD);
relDims = new FODimension(0, 0);
absoluteCTM = CTM.getCTMandRelDims(
getBlockContainerFO().getReferenceOrientation(),
getBlockContainerFO().getWritingMode(),
rect, relDims);
MinOptMax range = new MinOptMax(relDims.ipd);
BlockContainerBreaker breaker = new BlockContainerBreaker(this, range);
breaker.doLayout(relDims.bpd);
boolean contentOverflows = breaker.isOverflow();
LinkedList returnList = new LinkedList();
if (!breaker.isEmpty()) {
usedBPD = relDims.bpd - breaker.getDifferenceOfFirstPart();
Position bcPosition = new BlockContainerPosition(this, breaker);
returnList.add(new KnuthBox(0, bcPosition, false));
//TODO Maybe check for page overflow when autoHeight=true
if (!autoHeight & (contentOverflows/*usedBPD > relDims.bpd*/)) {
log.warn("Contents overflow block-container viewport: clipping");
if (getBlockContainerFO().getOverflow() == EN_HIDDEN) {
clip = true;
} else if (getBlockContainerFO().getOverflow() == EN_ERROR_IF_OVERFLOW) {
//TODO Throw layout exception
clip = true;
}
}
}
setFinished(true);
return returnList;
}
private class BlockContainerPosition extends NonLeafPosition {
private BlockContainerBreaker breaker;
public BlockContainerPosition(LayoutManager lm, BlockContainerBreaker breaker) {
super(lm, null);
this.breaker = breaker;
}
public BlockContainerBreaker getBreaker() {
return this.breaker;
}
}
private class BlockContainerBreaker extends AbstractBreaker {
private BlockContainerLayoutManager bclm;
private MinOptMax ipd;
//Info for deferred adding of areas
private PageBreakingAlgorithm deferredAlg;
private BlockSequence deferredOriginalList;
private BlockSequence deferredEffectiveList;
public BlockContainerBreaker(BlockContainerLayoutManager bclm, MinOptMax ipd) {
this.bclm = bclm;
this.ipd = ipd;
}
public int getDifferenceOfFirstPart() {
PageBreakPosition pbp = (PageBreakPosition)this.deferredAlg.getPageBreaks().getFirst();
return pbp.difference;
}
public boolean isOverflow() {
if (isEmpty()) {
return false;
} else {
return (deferredAlg.getPageBreaks().size() > 1);
}
}
protected LayoutManager getTopLevelLM() {
return bclm;
}
protected LayoutContext createLayoutContext() {
LayoutContext lc = super.createLayoutContext();
lc.setRefIPD(ipd.opt);
return lc;
}
protected LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
LayoutManager curLM; // currently active LM
LinkedList returnList = new LinkedList();
while ((curLM = getChildLM()) != null) {
LayoutContext childLC = new LayoutContext(0);
childLC.setStackLimit(context.getStackLimit());
childLC.setRefIPD(context.getRefIPD());
LinkedList returnedList = null;
if (!curLM.isFinished()) {
returnedList = curLM.getNextKnuthElements(childLC, alignment);
}
if (returnedList != null) {
bclm.wrapPositionElements(returnedList, returnList);
//returnList.addAll(returnedList);
}
}
setFinished(true);
return returnList;
}
protected int getCurrentDisplayAlign() {
return getBlockContainerFO().getDisplayAlign();
}
protected boolean hasMoreContent() {
return !isFinished();
}
protected void addAreas(PositionIterator posIter, LayoutContext context) {
AreaAdditionUtil.addAreas(posIter, context);
}
protected void doPhase3(PageBreakingAlgorithm alg, int partCount,
BlockSequence originalList, BlockSequence effectiveList) {
//Defer adding of areas until addAreas is called by the parent LM
this.deferredAlg = alg;
this.deferredOriginalList = originalList;
this.deferredEffectiveList = effectiveList;
}
protected void finishPart() {
//nop for bclm
}
protected LayoutManager getCurrentChildLM() {
return curChildLM;
}
public void addContainedAreas() {
if (isEmpty()) {
return;
}
//Rendering all parts (not just the first) at once for the case where the parts that
//overflow should be visible.
//TODO Check if this has any unwanted side-effects. Feels a bit like a hack.
addAreas(this.deferredAlg,
/*1*/ this.deferredAlg.getPageBreaks().size(),
this.deferredOriginalList, this.deferredEffectiveList);
}
}
/**
* @see org.apache.fop.layoutmgr.LayoutManager#getNextBreakPoss(org.apache.fop.layoutmgr.LayoutContext)
*/
public BreakPoss getNextBreakPoss(LayoutContext context) {
if (isAbsoluteOrFixed()) {
return getAbsoluteBreakPoss(context);
}
autoHeight = false;
boolean rotated = (getBlockContainerFO().getReferenceOrientation() % 180 != 0); //vals[0] == 0.0;
referenceIPD = context.getRefIPD();
int maxbpd = context.getStackLimit().opt;
int allocBPD, allocIPD;
if (height.getEnum() != EN_AUTO) {
allocBPD = height.getValue(); //this is the content-height
allocBPD += getBPIndents();
} else {
allocBPD = maxbpd;
autoHeight = true;
}
if (width.getEnum() != EN_AUTO) {
allocIPD = width.getValue(); //this is the content-width
allocIPD += getIPIndents();
} else {
allocIPD = referenceIPD;
}
vpContentBPD = allocBPD - getBPIndents();
vpContentIPD = allocIPD - getIPIndents();
double contentRectOffsetX = 0;
contentRectOffsetX += getBlockContainerFO().getCommonMarginBlock().startIndent.getValue();
double contentRectOffsetY = 0;
//contentRectOffsetY += getBlockContainerFO().getCommonMarginBlock().startIndent.getValue();
//contentRectOffsetY += getSpaceBefore();
contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getPaddingBefore(false);
Rectangle2D rect = new Rectangle2D.Double(
contentRectOffsetX, contentRectOffsetY,
vpContentIPD, vpContentBPD);
relDims = new FODimension(0, 0);
absoluteCTM = CTM.getCTMandRelDims(getBlockContainerFO().getReferenceOrientation(),
getBlockContainerFO().getWritingMode(), rect, relDims);
//double[] vals = absoluteCTM.toArray();
MinOptMax stackLimit;
if (rotated) {
// rotated 90 degrees
/*
if (relDims.ipd > context.getRefIPD()) {
relDims.ipd = context.getRefIPD();
}*/
//stackLimit = new MinOptMax(relDims.ipd);
/*
if (width.getEnum() == EN_AUTO) {
relDims.bpd = context.getStackLimit().opt;
}
absoluteCTM = new CTM(vals[0], vals[1], vals[2], vals[3], 0, 0);
*/
//absoluteCTM = new CTM(vals[0], vals[1], vals[2], vals[3], vals[5], vals[4]);
} else {
/*
if (vals[0] == -1.0) {
absoluteCTM = new CTM(vals[0], vals[1], vals[2], vals[3], 0, 0);
}*/
//stackLimit = context.getStackLimit();
}
stackLimit = new MinOptMax(relDims.bpd);
LayoutManager curLM; // currently active LM
MinOptMax stackSize = new MinOptMax();
// if starting add space before
// stackSize.add(spaceBefore);
BreakPoss lastPos = null;
//TODO fix layout dimensions!
getBlockContainerFO().setLayoutDimension(PercentBase.BLOCK_IPD, allocIPD);
getBlockContainerFO().setLayoutDimension(PercentBase.BLOCK_BPD, allocBPD);
getBlockContainerFO().setLayoutDimension(PercentBase.REFERENCE_AREA_IPD, relDims.ipd);
getBlockContainerFO().setLayoutDimension(PercentBase.REFERENCE_AREA_BPD, relDims.bpd);
while ((curLM = getChildLM()) != null) {
//Treat bc with fixed BPD as non-breakable
if (!autoHeight && (stackLimit.max > context.stackLimit.max)) {
if (log.isDebugEnabled()) {
log.debug("block-container does not fit in the available area "
+ "(available: " + context.stackLimit.max
+ ", needed: " + stackLimit.max + ")");
}
BreakPoss breakPoss = new BreakPoss(new LeafPosition(this, -1));
breakPoss.setFlag(BreakPoss.NEXT_OVERFLOWS, true);
breakPoss.setStackingSize(new MinOptMax());
return breakPoss;
}
// Make break positions and return blocks!
// Set up a LayoutContext
BreakPoss bp;
LayoutContext childLC = new LayoutContext(0);
childLC.setStackLimit(
MinOptMax.subtract(stackLimit,
stackSize));
childLC.setRefIPD(relDims.ipd);
boolean over = false;
while (!curLM.isFinished()) {
if ((bp = curLM.getNextBreakPoss(childLC)) != null) {
if (stackSize.opt + bp.getStackingSize().opt > stackLimit.max) {
// reset to last break
if (lastPos != null) {
reset(lastPos.getPosition());
} else {
curLM.resetPosition(null);
}
over = true;
break;
}
if (bp.nextBreakOverflows()) {
over = true;
break;
}
stackSize.add(bp.getStackingSize());
lastPos = bp;
childBreaks.add(bp);
childLC.setStackLimit(MinOptMax.subtract(
stackLimit, stackSize));
}
}
if (!rotated && autoHeight) {
BreakPoss breakPoss;
breakPoss = new BreakPoss(new LeafPosition(this,
childBreaks.size() - 1));
breakPoss.setStackingSize(stackSize);
if (over) {
breakPoss.setFlag(BreakPoss.NEXT_OVERFLOWS, true);
}
return breakPoss;
}
}
setFinished(true);
if (rotated) {
BreakPoss breakPoss;
breakPoss = new BreakPoss(new LeafPosition(this,
childBreaks.size() - 1));
breakPoss.setStackingSize(new MinOptMax(relDims.ipd));
return breakPoss;
}
if (!rotated && !autoHeight) {
//Treated as if all content is kept together
BreakPoss breakPoss;
breakPoss = new BreakPoss(new LeafPosition(this,
childBreaks.size() - 1));
breakPoss.setStackingSize(new MinOptMax(relDims.bpd));
return breakPoss;
}
return null;
}
private Point getAbsOffset() {
int x = 0;
int y = 0;
if (abProps.left.getEnum() != EN_AUTO) {
x = abProps.left.getValue();
}
if (abProps.top.getEnum() != EN_AUTO) {
y = abProps.top.getValue();
}
return new Point(x, y);
}
/**
* Generate and return the next break possibility for absolutely positioned
* block-containers.
* @param context LayoutContext to work with
* @return the next break position
* @see org.apache.fop.layoutmgr.LayoutManager#getNextBreakPoss(org.apache.fop.layoutmgr.LayoutContext)
*/
public BreakPoss getAbsoluteBreakPoss(LayoutContext context) {
LayoutManager curLM; // currently active LM
MinOptMax stackSize = new MinOptMax();
autoHeight = false;
Point offset = getAbsOffset();
int allocBPD, allocIPD;
if (height.getEnum() != EN_AUTO) {
allocBPD = height.getValue(); //this is the content-height
allocBPD += getBPIndents();
} else {
allocBPD = 0;
if (abProps.bottom.getEnum() != EN_AUTO) {
if (isFixed()) {
allocBPD = (int)getCurrentPV().getViewArea().getHeight();
} else {
allocBPD = context.getStackLimit().opt;
}
allocBPD -= offset.y;
if (abProps.bottom.getEnum() != EN_AUTO) {
allocBPD -= abProps.bottom.getValue();
}
} else {
autoHeight = true;
}
}
if (width.getEnum() != EN_AUTO) {
allocIPD = width.getValue(); //this is the content-width
allocIPD += getIPIndents();
} else {
if (isFixed()) {
allocIPD = (int)getCurrentPV().getViewArea().getWidth();
} else {
allocIPD = context.getRefIPD();
}
if (abProps.left.getEnum() != EN_AUTO) {
allocIPD -= abProps.left.getValue();
}
if (abProps.right.getEnum() != EN_AUTO) {
allocIPD -= abProps.right.getValue();
}
}
vpContentBPD = allocBPD - getBPIndents();
vpContentIPD = allocIPD - getIPIndents();
double contentRectOffsetX = offset.getX();
contentRectOffsetX += getBlockContainerFO().getCommonMarginBlock().startIndent.getValue();
double contentRectOffsetY = offset.getY();
contentRectOffsetY += getSpaceBefore();
contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getPaddingBefore(false);
Rectangle2D rect = new Rectangle2D.Double(
contentRectOffsetX, contentRectOffsetY,
vpContentIPD, vpContentBPD);
relDims = new FODimension(0, 0);
absoluteCTM = CTM.getCTMandRelDims(
getBlockContainerFO().getReferenceOrientation(),
getBlockContainerFO().getWritingMode(),
rect, relDims);
while ((curLM = getChildLM()) != null) {
// Make break positions and return blocks!
// Set up a LayoutContext
BreakPoss bp;
LayoutContext childLC = new LayoutContext(0);
childLC.setStackLimit(new MinOptMax(1000000));
childLC.setRefIPD(relDims.ipd);
while (!curLM.isFinished()) {
if ((bp = curLM.getNextBreakPoss(childLC)) != null) {
stackSize.add(bp.getStackingSize());
childBreaks.add(bp);
}
}
}
setFinished(true);
BreakPoss breakPoss = new BreakPoss(
new LeafPosition(this, childBreaks.size() - 1));
// absolutely positioned areas do not contribute
// to the normal stacking
breakPoss.setStackingSize(new MinOptMax(0));
usedBPD = stackSize.opt;
//TODO Maybe check for page overflow when autoHeight=true
if (!autoHeight & (usedBPD > relDims.bpd)) {
log.warn("Contents overflow block-container viewport: clipping");
if (getBlockContainerFO().getOverflow() == EN_HIDDEN) {
clip = true;
} else if (getBlockContainerFO().getOverflow() == EN_ERROR_IF_OVERFLOW) {
//TODO Throw layout exception
clip = true;
}
}
return breakPoss;
}
/**
* @see org.apache.fop.layoutmgr.LayoutManager#addAreas(org.apache.fop.layoutmgr.PositionIterator, org.apache.fop.layoutmgr.LayoutContext)
*/
public void addAreas(PositionIterator parentIter,
LayoutContext layoutContext) {
getParentArea(null);
// if this will create the first block area in a page
// and display-align is bottom or center, add space before
if (layoutContext.getSpaceBefore() > 0) {
addBlockSpacing(0.0, new MinOptMax(layoutContext.getSpaceBefore()));
}
getPSLM().addIDToPage(getBlockContainerFO().getId());
//addMarkersToPV(true, bp1.isFirstArea(), bp1.isLastArea());
getCurrentPV().addMarkers(markers, true, true, false);
LayoutManager childLM = null;
LayoutManager lastLM = null;
LayoutContext lc = new LayoutContext(0);
// set space after in the LayoutContext for children
if (layoutContext.getSpaceAfter() > 0) {
lc.setSpaceAfter(layoutContext.getSpaceAfter());
}
BlockContainerPosition bcpos = null;
PositionIterator childPosIter;
// "unwrap" the NonLeafPositions stored in parentIter
// and put them in a new list;
LinkedList positionList = new LinkedList();
Position pos;
boolean bSpaceBefore = false;
boolean bSpaceAfter = false;
while (parentIter.hasNext()) {
pos = (Position) parentIter.next();
/* LF *///System.out.println("pos = " + pos.getClass().getName());
Position innerPosition = ((NonLeafPosition) pos).getPosition();
if (pos instanceof BlockContainerPosition) {
if (bcpos != null) {
throw new IllegalStateException("Only one BlockContainerPosition allowed");
}
bcpos = (BlockContainerPosition)pos;
//Add child areas inside the reference area
//bcpos.getBreaker().addContainedAreas();
} else if (innerPosition == null) {
// pos was created by this BCLM and was inside an element
// representing space before or after
// this means the space was not discarded
if (positionList.size() == 0) {
// pos was in the element representing space-before
bSpaceBefore = true;
/* LF *///System.out.println(" space-before");
} else {
// pos was in the element representing space-after
bSpaceAfter = true;
/* LF *///System.out.println(" space-after");
}
} else if (innerPosition.getLM() == this
&& !(innerPosition instanceof MappingPosition)) {
// pos was created by this BlockLM and was inside a penalty
// allowing or forbidding a page break
// nothing to do
/* LF *///System.out.println(" penalty");
} else {
// innerPosition was created by another LM
positionList.add(innerPosition);
lastLM = innerPosition.getLM();
/* LF *///System.out.println(" " +
// innerPosition.getClass().getName());
}
}
if (bcpos == null) {
if (bpUnit == 0) {
// the Positions in positionList were inside the elements
// created by the LineLM
childPosIter = new StackingIter(positionList.listIterator());
} else {
// the Positions in positionList were inside the elements
// created by the BCLM in the createUnitElements() method
//if (((Position) positionList.getLast()) instanceof
// LeafPosition) {
// // the last item inside positionList is a LeafPosition
// // (a LineBreakPosition, more precisely); this means that
// // the whole paragraph is on the same page
// System.out.println("paragrafo intero");
// childPosIter = new KnuthPossPosIter(storedList, 0,
// storedList.size());
//} else {
// // the last item inside positionList is a Position;
// // this means that the paragraph has been split
// // between consecutive pages
LinkedList splitList = new LinkedList();
int splitLength = 0;
int iFirst = ((MappingPosition) positionList.getFirst()).getFirstIndex();
int iLast = ((MappingPosition) positionList.getLast()).getLastIndex();
// copy from storedList to splitList all the elements from
// iFirst to iLast
ListIterator storedListIterator = storedList.listIterator(iFirst);
while (storedListIterator.nextIndex() <= iLast) {
KnuthElement element = (KnuthElement) storedListIterator
.next();
// some elements in storedList (i.e. penalty items) were created
// by this BlockLM, and must be ignored
if (element.getLayoutManager() != this) {
splitList.add(element);
splitLength += element.getW();
lastLM = element.getLayoutManager();
}
}
//System.out.println("addAreas riferito a storedList da " +
// iFirst + " a " + iLast);
//System.out.println("splitLength= " + splitLength
// + " (" + neededUnits(splitLength) + " unita') "
// + (neededUnits(splitLength) * bpUnit - splitLength) + " spazi");
// add space before and / or after the paragraph
// to reach a multiple of bpUnit
if (bSpaceBefore && bSpaceAfter) {
foBlockSpaceBefore = new SpaceVal(getBlockContainerFO().getCommonMarginBlock().spaceBefore).getSpace();
foBlockSpaceAfter = new SpaceVal(getBlockContainerFO().getCommonMarginBlock().spaceAfter).getSpace();
adjustedSpaceBefore = (neededUnits(splitLength
+ foBlockSpaceBefore.min
+ foBlockSpaceAfter.min)
* bpUnit - splitLength) / 2;
adjustedSpaceAfter = neededUnits(splitLength
+ foBlockSpaceBefore.min
+ foBlockSpaceAfter.min)
* bpUnit - splitLength - adjustedSpaceBefore;
} else if (bSpaceBefore) {
adjustedSpaceBefore = neededUnits(splitLength
+ foBlockSpaceBefore.min)
* bpUnit - splitLength;
} else {
adjustedSpaceAfter = neededUnits(splitLength
+ foBlockSpaceAfter.min)
* bpUnit - splitLength;
}
//System.out.println("spazio prima = " + adjustedSpaceBefore
// + " spazio dopo = " + adjustedSpaceAfter + " totale = " +
// (adjustedSpaceBefore + adjustedSpaceAfter + splitLength));
childPosIter = new KnuthPossPosIter(splitList, 0, splitList
.size());
//}
}
// if adjusted space before
if (bSpaceBefore) {
addBlockSpacing(0, new MinOptMax(adjustedSpaceBefore));
}
while ((childLM = childPosIter.getNextChildLM()) != null) {
// set last area flag
lc.setFlags(LayoutContext.LAST_AREA,
(layoutContext.isLastArea() && childLM == lastLM));
/*LF*/lc.setStackLimit(layoutContext.getStackLimit());
// Add the line areas to Area
childLM.addAreas(childPosIter, lc);
}
} else {
// if adjusted space before
if (bSpaceBefore) {
addBlockSpacing(0, new MinOptMax(adjustedSpaceBefore));
}
//Add child areas inside the reference area
bcpos.getBreaker().addContainedAreas();
}
int bIndents = getBlockContainerFO().getCommonBorderPaddingBackground().getBPPaddingAndBorder(false);
getCurrentPV().addMarkers(markers, false, false, true);
flush();
// if adjusted space after
if (bSpaceAfter) {
addBlockSpacing(0, new MinOptMax(adjustedSpaceAfter));
}
viewportBlockArea = null;
referenceArea = null;
}
public void addAreasOLDOLDOLD(PositionIterator parentIter,
LayoutContext layoutContext) {
getParentArea(null);
/* taken from BlockLM, check first if we should use space-before|after traits
double adjust = layoutContext.getSpaceAdjust();
if (!isAbsoluteOrFixed()) {
// if adjusted space before
addBlockSpacing(adjust, foBlockSpaceBefore);
foBlockSpaceBefore = null;
}*/
BreakPoss bp1 = (BreakPoss)parentIter.peekNext();
getPSLM().addIDToPage(getBlockContainerFO().getId());
getCurrentPV().addMarkers(markers, true, bp1.isFirstArea(), bp1.isLastArea());
LayoutManager childLM;
int iStartPos = 0;
LayoutContext lc = new LayoutContext(0);
while (parentIter.hasNext()) {
LeafPosition lfp = (LeafPosition) parentIter.next();
// Add the block areas to Area
PositionIterator breakPosIter
= new BreakPossPosIter(childBreaks, iStartPos,
lfp.getLeafPos() + 1);
iStartPos = lfp.getLeafPos() + 1;
while ((childLM = breakPosIter.getNextChildLM()) != null) {
childLM.addAreas(breakPosIter, lc);
}
}
flush();
getCurrentPV().addMarkers(markers, true, bp1.isFirstArea(), bp1.isLastArea());
/*
if (!isAbsoluteOrFixed()) {
// if adjusted space after
foBlockSpaceAfter = new SpaceVal(getBlockContainerFO().getCommonMarginBlock().spaceAfter).getSpace();
addBlockSpacing(adjust, foBlockSpaceAfter);
}*/
childBreaks.clear();
viewportBlockArea = null;
referenceArea = null;
}
/**
* Get the parent area for children of this block container.
* This returns the current block container area
* and creates it if required.
*
* @see org.apache.fop.layoutmgr.LayoutManager#getParentArea(Area)
*/
public Area getParentArea(Area childArea) {
if (referenceArea == null) {
viewportBlockArea = new BlockViewport();
viewportBlockArea.addTrait(Trait.IS_VIEWPORT_AREA, Boolean.TRUE);
viewportBlockArea.setIPD(vpContentIPD);
if (autoHeight) {
viewportBlockArea.setBPD(0);
} else {
viewportBlockArea.setBPD(vpContentBPD);
}
TraitSetter.addBorders(viewportBlockArea, getBlockContainerFO().getCommonBorderPaddingBackground());
TraitSetter.addBackground(viewportBlockArea, getBlockContainerFO().getCommonBorderPaddingBackground());
TraitSetter.addMargins(viewportBlockArea,
getBlockContainerFO().getCommonBorderPaddingBackground(),
getBlockContainerFO().getCommonMarginBlock());
viewportBlockArea.setCTM(absoluteCTM);
viewportBlockArea.setClip(clip);
if (getSpaceBefore() != 0) {
viewportBlockArea.addTrait(Trait.SPACE_BEFORE, new Integer(getSpaceBefore()));
}
if (foBlockSpaceAfter.opt != 0) {
viewportBlockArea.addTrait(Trait.SPACE_AFTER, new Integer(foBlockSpaceAfter.opt));
}
if (abProps.absolutePosition == EN_ABSOLUTE
|| abProps.absolutePosition == EN_FIXED) {
Point offset = getAbsOffset();
viewportBlockArea.setXOffset(offset.x);
viewportBlockArea.setYOffset(offset.y);
} else {
//nop
}
referenceArea = new Block();
referenceArea.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
if (abProps.absolutePosition == EN_ABSOLUTE) {
viewportBlockArea.setPositioning(Block.ABSOLUTE);
} else if (abProps.absolutePosition == EN_FIXED) {
viewportBlockArea.setPositioning(Block.FIXED);
}
// Set up dimensions
// Must get dimensions from parent area
/*Area parentArea =*/ parentLM.getParentArea(referenceArea);
//int referenceIPD = parentArea.getIPD();
referenceArea.setIPD(relDims.ipd);
// Get reference IPD from parentArea
setCurrentArea(viewportBlockArea); // ??? for generic operations
}
return referenceArea;
}
/**
* Add the child to the block container.
*
* @see org.apache.fop.layoutmgr.LayoutManager#addChildArea(Area)
*/
public void addChildArea(Area childArea) {
if (referenceArea != null) {
referenceArea.addBlock((Block) childArea);
}
}
/**
* @see org.apache.fop.layoutmgr.LayoutManager#resetPosition(org.apache.fop.layoutmgr.Position)
*/
public void resetPosition(Position resetPos) {
if (resetPos == null) {
reset(null);
}
}
/**
* Force current area to be added to parent area.
* @see org.apache.fop.layoutmgr.AbstractLayoutManager#flush()
*/
protected void flush() {
viewportBlockArea.addBlock(referenceArea, autoHeight);
//Handle display-align now that the used BPD can be determined
usedBPD = referenceArea.getAllocBPD();
/* done by the breaker now by inserting additional boxes
if (!autoHeight & (usedBPD > 0)) {
if (getBlockContainerFO().getDisplayAlign() == EN_CENTER) {
viewportBlockArea.setCTM(viewportBlockArea.getCTM().multiply(
new CTM().translate(0, (relDims.bpd - usedBPD) / 2)));
} else if (getBlockContainerFO().getDisplayAlign() == EN_AFTER) {
viewportBlockArea.setCTM(viewportBlockArea.getCTM().multiply(
new CTM().translate(0, (relDims.bpd - usedBPD))));
}
}*/
// Fake a 0 height for absolute positioned blocks.
int saveBPD = viewportBlockArea.getBPD();
if (viewportBlockArea.getPositioning() == Block.ABSOLUTE) {
viewportBlockArea.setBPD(0);
}
super.flush();
// Restore the right height.
if (viewportBlockArea.getPositioning() == Block.ABSOLUTE) {
viewportBlockArea.setBPD(saveBPD);
}
}
/**
* @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#negotiateBPDAdjustment(int, org.apache.fop.layoutmgr.KnuthElement)
*/
public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
// TODO Auto-generated method stub
return 0;
}
/**
* @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#discardSpace(org.apache.fop.layoutmgr.KnuthGlue)
*/
public void discardSpace(KnuthGlue spaceGlue) {
// TODO Auto-generated method stub
}
/**
* @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether()
*/
public boolean mustKeepTogether() {
//TODO Keeps will have to be more sophisticated sooner or later
return ((BlockLevelLayoutManager)getParent()).mustKeepTogether()
|| !getBlockContainerFO().getKeepTogether().getWithinPage().isAuto()
|| !getBlockContainerFO().getKeepTogether().getWithinColumn().isAuto();
}
/**
* @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious()
*/
public boolean mustKeepWithPrevious() {
return !getBlockContainerFO().getKeepWithPrevious().getWithinPage().isAuto()
|| !getBlockContainerFO().getKeepWithPrevious().getWithinColumn().isAuto();
}
/**
* @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext()
*/
public boolean mustKeepWithNext() {
return !getBlockContainerFO().getKeepWithNext().getWithinPage().isAuto()
|| !getBlockContainerFO().getKeepWithNext().getWithinColumn().isAuto();
}
/**
* convenience method that returns the BlockContainer node
*/
protected BlockContainer getBlockContainerFO() {
return (BlockContainer) fobj;
}
}