/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2001 - 2009 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.layout.process;
import org.pentaho.reporting.engine.classic.core.ElementAlignment;
import org.pentaho.reporting.engine.classic.core.layout.model.FinishedRenderNode;
import org.pentaho.reporting.engine.classic.core.layout.model.LayoutNodeTypes;
import org.pentaho.reporting.engine.classic.core.layout.model.LogicalPageBox;
import org.pentaho.reporting.engine.classic.core.layout.model.ParagraphRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderLength;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderNode;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderableReplacedContentBox;
import org.pentaho.reporting.engine.classic.core.layout.model.WatermarkAreaBox;
import org.pentaho.reporting.engine.classic.core.layout.model.context.BoxDefinition;
import org.pentaho.reporting.engine.classic.core.layout.model.context.StaticBoxLayoutProperties;
import org.pentaho.reporting.engine.classic.core.style.ElementStyleKeys;
import org.pentaho.reporting.engine.classic.core.util.geom.StrictGeomUtility;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* This processes the second step of the vertical-layouting.
* <p/>
* At this point, the static height of all elements is known (that is the height of all elements that do not use
* percentages in either the y or height properties).
* <p/>
* That height is then used as base-value to resolve all relative heights and y positions and the layouting is redone.
*
* @author Thomas Morgner
*/
public final class CanvasMajorAxisLayoutStep extends IterateVisualProcessStep
{
private static final boolean PARANOID_CHECKS = true;
private static final Log logger = LogFactory.getLog(CanvasMajorAxisLayoutStep.class);
// Set the maximum height to an incredibly high value. This is now 2^43 micropoints or more than
// 3000 kilometers. Please call me directly at any time if you need more space for printing.
private static final long MAX_AUTO = StrictGeomUtility.toInternalValue(0x80000000000L);
public CanvasMajorAxisLayoutStep()
{
}
public void compute(final LogicalPageBox pageBox)
{
startProcessing(pageBox);
}
private long resolveParentHeight(final RenderNode node)
{
final RenderBox parent = node.getParent();
if (parent == null)
{
if (node.getNodeType() == LayoutNodeTypes.TYPE_BOX_WATERMARK)
{
final LogicalPageBox box = node.getLogicalPage();
if (box != null)
{
return box.getPageHeight();
}
}
return 0;
}
return parent.getCachedHeight();
}
/**
* Continues processing. The renderbox must have a valid x-layout (that is: X, content-X1, content-X2 and Width)
*
* @param box
*/
public void continueComputation(final RenderBox box)
{
// This is most-likely wrong, but as we do not support inline-block elements yet, we can ignore this for now.
if (box.getContentAreaX2() == 0 || box.getCachedWidth() == 0)
{
throw new IllegalStateException("Box must be layouted a bit ..");
}
startProcessing(box);
}
protected boolean startBlockLevelBox(final RenderBox box)
{
if (box.isCacheValid())
{
return false;
}
final long oldPosition = box.getCachedY();
// Compute the block-position of the box. The box is positioned relative to the previous silbling or
// relative to the parent.
box.setCachedY(computeVerticalBlockPosition(box));
if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_WATERMARK)
{
final WatermarkAreaBox watermarkAreaBox = (WatermarkAreaBox) box;
box.setCachedHeight(watermarkAreaBox.getLogicalPage().getPageHeight());
}
else
{
final RenderBox watermark = isWatermark(box);
if (watermark != null)
{
final WatermarkAreaBox watermarkAreaBox = (WatermarkAreaBox) watermark;
box.setCachedHeight(watermarkAreaBox.getLogicalPage().getPageHeight());
}
else if ((box.getNodeType() & LayoutNodeTypes.MASK_BOX_BLOCK) == LayoutNodeTypes.MASK_BOX_BLOCK)
{
final long blockHeight = computeBlockHeightAndAlign(box, 0);
box.setCachedHeight(blockHeight);
}
else if ((box.getNodeType() & LayoutNodeTypes.MASK_BOX_ROW) == LayoutNodeTypes.MASK_BOX_ROW)
{
final long blockHeight = computeRowHeightAndAlign(box, 0);
box.setCachedHeight(blockHeight);
}
else if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_CONTENT)
{
// todo: RPC: CACHED-VS-COMPUTED
final RenderableReplacedContentBox rpc = (RenderableReplacedContentBox) box;
box.setCachedHeight(ReplacedContentUtil.computeHeight(rpc, 0, box.getCachedWidth()));
}
else
{
final long cachedHeight = computeCanvasHeight(box, true);
box.setCachedHeight(cachedHeight);
}
}
if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_PARAGRAPH)
{
if (box.getCachedY() != oldPosition)
{
CacheBoxShifter.shiftBoxChilds(box, box.getCachedY() - oldPosition);
}
}
return true;
}
private RenderBox isWatermark(final RenderBox box)
{
final RenderBox parent = box.getParent();
if (parent == null)
{
return null;
}
if (parent.getNodeType() == LayoutNodeTypes.TYPE_BOX_WATERMARK)
{
return parent;
}
final RenderBox parent2 = parent.getParent();
if (parent2 == null)
{
return null;
}
if (parent2.getNodeType() == LayoutNodeTypes.TYPE_BOX_WATERMARK)
{
return parent2;
}
return null;
}
protected void processBlockLevelNode(final RenderNode node)
{
// This could be anything, text, or an image.
node.setCachedY(computeVerticalBlockPosition(node));
final int type = node.getNodeType();
if (type == LayoutNodeTypes.TYPE_NODE_FINISHEDNODE)
{
final FinishedRenderNode fnode = (FinishedRenderNode) node;
node.setCachedHeight(fnode.getLayoutedHeight());
}
else if ((type & LayoutNodeTypes.MASK_BOX_INLINE) == LayoutNodeTypes.MASK_BOX_INLINE)
{
throw new IllegalStateException("A Inline-Box must be contained in a paragraph.");
}
}
protected void finishBlockLevelBox(final RenderBox box)
{
if (box.isCacheValid())
{
return;
}
final int nodeType = box.getNodeType();
if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_WATERMARK)
{
final WatermarkAreaBox watermarkAreaBox = (WatermarkAreaBox) box;
box.setCachedHeight(watermarkAreaBox.getLogicalPage().getPageHeight());
}
else
{
final RenderBox watermark = isWatermark(box);
if (watermark != null)
{
final WatermarkAreaBox watermarkAreaBox = (WatermarkAreaBox) watermark;
box.setCachedHeight(watermarkAreaBox.getLogicalPage().getPageHeight());
}
else if ((nodeType & LayoutNodeTypes.MASK_BOX_BLOCK) == LayoutNodeTypes.MASK_BOX_BLOCK)
{
final long blockHeight = computeBlockHeightAndAlign(box, 0);
box.setCachedHeight(blockHeight);
}
else if ((nodeType & LayoutNodeTypes.MASK_BOX_ROW) == LayoutNodeTypes.MASK_BOX_ROW)
{
final long blockHeight = computeRowHeightAndAlign(box, 0);
box.setCachedHeight(blockHeight);
}
else if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_CONTENT)
{
// do nothing ..
}
else
{
box.setCachedHeight(computeCanvasHeight(box, true));
}
}
}
private long computeVerticalBlockPosition(final RenderNode node)
{
// we have no margins yet ..
final long marginTop = 0;
// The y-position of a box depends on the parent.
final RenderBox parent = node.getParent();
// A table row is something special. Although it is a block box,
// it layouts its children from left to right
if (parent == null)
{
return marginTop;
}
if ((parent.getNodeType() & LayoutNodeTypes.MASK_BOX_BLOCK) == LayoutNodeTypes.MASK_BOX_BLOCK)
{
final RenderNode prev = node.getPrev();
if (prev != null)
{
// we have a silbling. Position yourself directly below your silbling ..
return (marginTop + prev.getCachedY() + prev.getCachedHeight());
}
else
{
final StaticBoxLayoutProperties blp = parent.getStaticBoxLayoutProperties();
final BoxDefinition bdef = parent.getBoxDefinition();
final long insetTop = (blp.getBorderTop() + bdef.getPaddingTop());
return (marginTop + insetTop + parent.getCachedY());
}
}
else
{
// there's no parent ..
return (marginTop);
}
}
private long computeBlockHeightAndAlign(final RenderBox box, final long resolveSize)
{
if (resolveSize < 0)
{
throw new IllegalArgumentException("ResovleSize cannot be negative");
}
// Check the height. Set the height.
final BoxDefinition boxDefinition = box.getBoxDefinition();
final RenderLength preferredHeight = boxDefinition.getPreferredHeight();
final RenderLength minimumHeight = boxDefinition.getMinimumHeight();
final RenderLength maximumHeight = boxDefinition.getMaximumHeight();
final long usedHeight;
final long childY2;
final long childY1;
final RenderNode lastChildNode = box.getLastChild();
if (lastChildNode != null)
{
childY1 = box.getFirstChild().getCachedY();
childY2 = lastChildNode.getCachedY() + lastChildNode.getCachedHeight() + lastChildNode.getEffectiveMarginBottom();
usedHeight = (childY2 - childY1);
}
else
{
usedHeight = 0;
childY2 = 0;
childY1 = 0;
}
final long rminH = minimumHeight.resolve(resolveSize, 0);
final long rmaxH = maximumHeight.resolve(resolveSize, CanvasMajorAxisLayoutStep.MAX_AUTO);
final StaticBoxLayoutProperties blp = box.getStaticBoxLayoutProperties();
final long insetBottom = blp.getBorderBottom() + boxDefinition.getPaddingBottom();
final long insetTop = blp.getBorderTop() + boxDefinition.getPaddingTop();
//todo
final long computedContentHeight;
if (boxDefinition.isSizeSpecifiesBorderBox())
{
final long rprefH = preferredHeight.resolve(resolveSize, usedHeight + insetTop + insetBottom);
final long specifiedHeight = ProcessUtility.computeLength(rminH, rmaxH, rprefH);
computedContentHeight = specifiedHeight - insetTop - insetBottom;
}
else
{
final long rprefH = preferredHeight.resolve(resolveSize, usedHeight);
computedContentHeight = ProcessUtility.computeLength(rminH, rmaxH, rprefH);
}
if (lastChildNode != null)
{
// grab the node's y2
if (computedContentHeight > usedHeight)
{
// we have extra space to distribute. So lets shift some boxes.
final ElementAlignment valign = box.getNodeLayoutProperties().getVerticalAlignment();
if (ElementAlignment.BOTTOM.equals(valign))
{
final long boxBottom = (box.getCachedY() + computedContentHeight - insetBottom);
final long delta = boxBottom - childY2;
CacheBoxShifter.shiftBoxChilds(box, delta);
}
else if (ElementAlignment.MIDDLE.equals(valign))
{
final long extraHeight = computedContentHeight - usedHeight;
final long boxTop = box.getCachedY() + insetTop + (extraHeight / 2);
final long delta = boxTop - childY1;
CacheBoxShifter.shiftBoxChilds(box, delta);
}
}
}
final long retval = Math.max(0, computedContentHeight + insetTop + insetBottom);
// For the water-mark area, this computation is different. The Watermark-area uses the known height of
// the parent (=the page size)
if (box instanceof WatermarkAreaBox)
{
final WatermarkAreaBox watermarkAreaBox = (WatermarkAreaBox) box;
final LogicalPageBox lpb = watermarkAreaBox.getLogicalPage();
// set the page-height as watermark size.
return Math.max(retval, Math.max(0, lpb.getPageHeight() - insetTop - insetBottom));
}
return retval;
}
private long computeRowHeightAndAlign(final RenderBox box, final long resolveSize)
{
if (resolveSize < 0)
{
throw new IllegalArgumentException("ResovleSize cannot be negative");
}
// For the water-mark area, this computation is different. The Watermark-area uses the known height of
// the parent (=the page size)
if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_WATERMARK)
{
final WatermarkAreaBox watermarkAreaBox = (WatermarkAreaBox) box;
final LogicalPageBox lpb = watermarkAreaBox.getLogicalPage();
// set the page-height as watermark size.
return lpb.getPageHeight();
}
// Check the height. Set the height.
final BoxDefinition boxDefinition = box.getBoxDefinition();
final RenderLength preferredHeight = boxDefinition.getPreferredHeight();
final RenderLength minimumHeight = boxDefinition.getMinimumHeight();
final RenderLength maximumHeight = boxDefinition.getMaximumHeight();
final StaticBoxLayoutProperties blp = box.getStaticBoxLayoutProperties();
final long insetBottom = blp.getBorderBottom() + boxDefinition.getPaddingBottom();
final long insetTop = blp.getBorderTop() + boxDefinition.getPaddingTop();
final long usedHeight;
RenderNode child = box.getFirstChild();
// initialize with the values computed in the InfMajorStep
long maxChildY2 = box.getCachedY() + box.getCachedHeight();
if (child != null)
{
while (child != null)
{
maxChildY2 = Math.max(child.getCachedY() + child.getCachedHeight() + child.getEffectiveMarginBottom(),
maxChildY2);
child = child.getNext();
}
usedHeight = (maxChildY2 - (box.getCachedY() + insetTop));
}
else
{
usedHeight = 0;
}
final long rminH = minimumHeight.resolve(resolveSize, 0);
final long rmaxH = maximumHeight.resolve(resolveSize, CanvasMajorAxisLayoutStep.MAX_AUTO);
final long computedContentHeight;
if (boxDefinition.isSizeSpecifiesBorderBox())
{
final long rprefH = preferredHeight.resolve(resolveSize, usedHeight + insetTop + insetBottom);
final long specifiedHeight = ProcessUtility.computeLength(rminH, rmaxH, rprefH);
computedContentHeight = specifiedHeight - insetTop - insetBottom;
}
else
{
final long rprefH = preferredHeight.resolve(resolveSize, usedHeight);
computedContentHeight = ProcessUtility.computeLength(rminH, rmaxH, rprefH);
}
child = box.getFirstChild();
final ElementAlignment valign = box.getNodeLayoutProperties().getVerticalAlignment();
final long boxY1 = box.getCachedY() + insetTop;
final long boxY2 = boxY1 + computedContentHeight;
while (child != null)
{
final long childY1 = child.getCachedY();
final long childY2 = childY1 + child.getCachedHeight();
// we have extra space to distribute. So lets shift some boxes.
if (ElementAlignment.BOTTOM.equals(valign))
{
final long boxBottom = (boxY2 - insetBottom);
final long delta = boxBottom - childY2;
CacheBoxShifter.shiftBox(child, delta);
}
else if (ElementAlignment.MIDDLE.equals(valign))
{
final long extraHeight = computedContentHeight - (childY2 - childY1);
final long boxTop = boxY1 + (extraHeight / 2);
final long delta = boxTop - childY1;
CacheBoxShifter.shiftBox(child, delta);
}
child = child.getNext();
}
final long retval = Math.max(0, computedContentHeight + insetTop + insetBottom);
if (retval < 0)
{
throw new IllegalStateException("A child cannot exceed the area of the parent.");
}
if (retval == 0 && box.getCachedHeight() > 0)
{
throw new IllegalStateException("A child cannot exceed the area of the parent.");
}
return retval;
}
protected void processParagraphChilds(final ParagraphRenderBox box)
{
}
protected boolean startCanvasLevelBox(final RenderBox box)
{
if (box.isCacheValid())
{
return false;
}
final long oldPosition = box.getCachedY();
box.setCachedY(computeVerticalCanvasPosition(box));
if ((box.getNodeType() & LayoutNodeTypes.MASK_BOX_BLOCK) == LayoutNodeTypes.MASK_BOX_BLOCK)
{
final long resolveSize = resolveParentHeight(box);
final long blockHeight = computeBlockHeightAndAlign(box, resolveSize);
box.setCachedHeight(blockHeight);
}
else if ((box.getNodeType() & LayoutNodeTypes.MASK_BOX_ROW) == LayoutNodeTypes.MASK_BOX_ROW)
{
final long resolveSize = resolveParentHeight(box);
final long blockHeight = computeRowHeightAndAlign(box, resolveSize);
box.setCachedHeight(blockHeight);
}
else if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_CONTENT)
{
final long resolveSize = resolveUseableParentHeight(box);
final RenderableReplacedContentBox rpc = (RenderableReplacedContentBox) box;
final long computedHeight = ReplacedContentUtil.computeHeight(rpc, resolveSize, box.getCachedWidth());
box.setCachedHeight(computedHeight);
}
else
{
box.setCachedHeight(computeCanvasHeight(box, false));
}
if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_PARAGRAPH)
{
if (box.getCachedY() != oldPosition)
{
CacheBoxShifter.shiftBoxChilds(box, box.getCachedY() - oldPosition);
}
}
return true;
}
protected void processCanvasLevelNode(final RenderNode node)
{
node.setCachedY(computeVerticalCanvasPosition(node));
if (node.getNodeType() == LayoutNodeTypes.TYPE_NODE_FINISHEDNODE)
{
final FinishedRenderNode fnode = (FinishedRenderNode) node;
node.setCachedHeight(fnode.getLayoutedHeight());
}
else
{
node.setCachedHeight(0);
}
}
/**
* Finishes up a canvas level box. This updates/affects the height of the parent, as the canvas model defines that the
* parent always fully encloses all of its childs.
* <p/>
* When no preferred height is defined, the height of an element is the maximum of its minimum-height and the absolute
* height of all of its direct children.
* <p/>
* To resolve the value of percentages, the system uses the maximum of the parent's height and the maximum of all (y +
* height) of all children.)
*
* @param box
*/
protected void finishCanvasLevelBox(final RenderBox box)
{
if (box.isCacheValid())
{
return;
}
final int type = box.getNodeType();
if ((type & LayoutNodeTypes.MASK_BOX_BLOCK) == LayoutNodeTypes.MASK_BOX_BLOCK)
{
final long resolveSize = resolveParentHeight(box);
final long blockHeight = computeBlockHeightAndAlign(box, resolveSize);
box.setCachedHeight(blockHeight);
}
else if ((type & LayoutNodeTypes.MASK_BOX_ROW) == LayoutNodeTypes.MASK_BOX_ROW)
{
final long resolveSize = resolveParentHeight(box);
final long blockHeight = computeRowHeightAndAlign(box, resolveSize);
box.setCachedHeight(blockHeight);
}
else if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_CONTENT)
{
// do nothing ..
}
else
{
box.setCachedHeight(computeCanvasHeight(box, false));
}
}
private long computeVerticalCanvasPosition(final RenderNode node)
{
final RenderBox parent = node.getParent();
final long parentPosition;
if (parent == null)
{
parentPosition = 0;
}
else
{
final StaticBoxLayoutProperties blp = parent.getStaticBoxLayoutProperties();
final BoxDefinition bdef = parent.getBoxDefinition();
final long insetsTop = (blp.getBorderTop() + bdef.getPaddingTop());
parentPosition = parent.getCachedY() + insetsTop;
}
final double posY = node.getStyleSheet().getDoubleStyleProperty(ElementStyleKeys.POS_Y, 0);
if (node.isSizeSpecifiesBorderBox())
{
return (parentPosition + RenderLength.resolveLength(resolveParentHeight(node), posY));
}
else
{
final long insetsTop;
if ((node.getNodeType() & LayoutNodeTypes.MASK_BOX) == LayoutNodeTypes.MASK_BOX)
{
final RenderBox box = (RenderBox) node;
final StaticBoxLayoutProperties blp = box.getStaticBoxLayoutProperties();
final BoxDefinition bdef = box.getBoxDefinition();
insetsTop = (blp.getBorderTop() + bdef.getPaddingTop());
}
else
{
insetsTop = 0;
}
return (parentPosition + RenderLength.resolveLength(resolveParentHeight(node), posY) - insetsTop);
}
}
/**
* The usable parent height is computed as the nodes's computed y position up to the remaining parent height, not
* counting any of the parent's borders or paddings. It is assumed that the parent's top-insets already have been used
* to compute the node's y-position, so that we must ignore them here.
*
* @param node
* @return
*/
private long resolveUseableParentHeight(final RenderNode node)
{
final RenderBox parent = node.getParent();
if (parent == null)
{
return node.getCachedHeight();
}
final long height = parent.getCachedHeight();
final BoxDefinition bdef = parent.getBoxDefinition();
final StaticBoxLayoutProperties blp = parent.getStaticBoxLayoutProperties();
final long insetsBottom = blp.getBorderBottom() + bdef.getPaddingBottom();
final long parentAvailableHeight = (parent.getCachedY() + height - insetsBottom) - node.getCachedY();
if (PARANOID_CHECKS && isWatermark(parent) == null)
{
// the check is only valid if there is no preferred height
// a preferred height may create overflowing childs, as it limits the height of the box to the defined value
if (RenderLength.AUTO.equals(bdef.getPreferredHeight()))
{
// the check is only valid if there is no max height
// a max height may create overflowing childs, as it limits the height of the box to the defined value
final RenderLength maxHeight = bdef.getMaximumHeight();
if (RenderLength.AUTO.equals(maxHeight) ||
(maxHeight.isPercentage() == false && maxHeight.getValue() >= StrictGeomUtility.toInternalValue(
Short.MAX_VALUE)))
{
final long childConsumedHeight = parentAvailableHeight - node.getCachedHeight();
if (childConsumedHeight < 0)
{
logger.warn
("A child cannot exceed the area of the parent: " + node.getName() +
" Parent: " + parentAvailableHeight + " Child: " + childConsumedHeight);
}
}
}
}
return parentAvailableHeight;
}
//todo
private long computeCanvasHeight(final RenderBox box, final boolean heightResolvesToZero)
{
final StaticBoxLayoutProperties blp = box.getStaticBoxLayoutProperties();
final BoxDefinition bdef = box.getBoxDefinition();
final BoxDefinition boxDefinition = box.getBoxDefinition();
final RenderLength minHeight = boxDefinition.getMinimumHeight();
final RenderLength preferredHeight = boxDefinition.getPreferredHeight();
final RenderLength maxHeight = boxDefinition.getMaximumHeight();
final long insetsTop = (blp.getBorderTop() + bdef.getPaddingTop());
final long insetsBottom = blp.getBorderBottom() + bdef.getPaddingBottom();
final long insets = insetsTop + insetsBottom;
final long parentHeight;
final long usableParentHeight;
if (heightResolvesToZero)
{
parentHeight = 0;
usableParentHeight = 0;
}
else
{
parentHeight = Math.max(resolveParentHeight(box), box.getCachedHeight());
usableParentHeight = resolveUseableParentHeight(box);
}
// find the maximum of the used height (for all childs) and the specified min-height.
long consumedHeight = Math.max(box.getCachedHeight(),
Math.min(minHeight.resolve(parentHeight), usableParentHeight));
// The consumed height computed above specifies the size at the border-edge.
// However, depending on the box-sizing property, we may have to resolve them against the
// content-edge instead.
if (box.isSizeSpecifiesBorderBox())
{
final long minHeightResolved = minHeight.resolve(parentHeight);
final long maxHeightResolved = maxHeight.resolve(parentHeight);
final long prefHeightResolved;
if (RenderLength.AUTO.equals(preferredHeight))
{
prefHeightResolved = consumedHeight;
}
else
{
prefHeightResolved = preferredHeight.resolve(parentHeight);
}
final long height = ProcessUtility.computeLength(minHeightResolved, maxHeightResolved, prefHeightResolved);
if (heightResolvesToZero)
return height;
return Math.min(height, usableParentHeight);
}
else
{
consumedHeight = Math.max(0, consumedHeight - insets);
final long minHeightResolved = minHeight.resolve(parentHeight);
final long maxHeightResolved = maxHeight.resolve(parentHeight);
final long prefHeightResolved;
if (RenderLength.AUTO.equals(preferredHeight))
{
prefHeightResolved = consumedHeight;
}
else
{
prefHeightResolved = preferredHeight.resolve(parentHeight);
}
final long height = ProcessUtility.computeLength(minHeightResolved, maxHeightResolved, prefHeightResolved);
if (heightResolvesToZero)
return height;
return Math.min(height + insets, usableParentHeight);
}
}
protected boolean startRowLevelBox(final RenderBox box)
{
if (box.isCacheValid())
{
return false;
}
final long oldPosition = box.getCachedY();
// Compute the block-position of the box. The box is positioned relative to the previous silbling or
// relative to the parent.
box.setCachedY(computeVerticalRowPosition(box));
if ((box.getNodeType() & LayoutNodeTypes.MASK_BOX_BLOCK) == LayoutNodeTypes.MASK_BOX_BLOCK)
{
final long resolveSize = resolveParentHeight(box);
final long blockHeight = computeBlockHeightAndAlign(box, resolveSize);
box.setCachedHeight(blockHeight);
}
else if ((box.getNodeType() & LayoutNodeTypes.MASK_BOX_ROW) == LayoutNodeTypes.MASK_BOX_ROW)
{
final long resolveSize = resolveParentHeight(box);
final long blockHeight = computeRowHeightAndAlign(box, resolveSize);
box.setCachedHeight(blockHeight);
}
else if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_CONTENT)
{
final RenderableReplacedContentBox rpc = (RenderableReplacedContentBox) box;
final long resolveSize = resolveParentHeight(box);
box.setCachedHeight(ReplacedContentUtil.computeHeight(rpc, resolveSize, box.getCachedWidth()));
}
else
{
final long cachedHeight = computeCanvasHeight(box, false);
box.setCachedHeight(cachedHeight);
}
if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_PARAGRAPH)
{
if (box.getCachedY() != oldPosition)
{
CacheBoxShifter.shiftBoxChilds(box, box.getCachedY() - oldPosition);
}
}
return true;
}
protected void processRowLevelNode(final RenderNode node)
{
// This could be anything, text, or an image.
node.setCachedY(computeVerticalRowPosition(node));
final int type = node.getNodeType();
if (type == LayoutNodeTypes.TYPE_NODE_FINISHEDNODE)
{
final FinishedRenderNode fnode = (FinishedRenderNode) node;
node.setCachedHeight(fnode.getLayoutedHeight());
}
else if ((type & LayoutNodeTypes.MASK_BOX_INLINE) == LayoutNodeTypes.MASK_BOX_INLINE)
{
throw new IllegalStateException("A Inline-Box must be contained in a paragraph.");
}
}
protected void finishRowLevelBox(final RenderBox box)
{
if (box.isCacheValid())
{
return;
}
final int nodeType = box.getNodeType();
if ((nodeType & LayoutNodeTypes.MASK_BOX_BLOCK) == LayoutNodeTypes.MASK_BOX_BLOCK)
{
final long resolveSize = resolveParentHeight(box);
final long blockHeight = computeBlockHeightAndAlign(box, resolveSize);
box.setCachedHeight(blockHeight);
}
else if ((nodeType & LayoutNodeTypes.MASK_BOX_ROW) == LayoutNodeTypes.MASK_BOX_ROW)
{
final long resolveSize = resolveParentHeight(box);
final long blockHeight = computeRowHeightAndAlign(box, resolveSize);
box.setCachedHeight(blockHeight);
}
else if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_CONTENT)
{
// do nothing ..
}
else
{
box.setCachedHeight(computeCanvasHeight(box, false));
}
}
private long computeVerticalRowPosition(final RenderNode node)
{
final RenderBox parent = node.getParent();
if (parent != null)
{
// the computed position of an inline-element must be the same as the position of the parent element.
// A inline-box always has an other inline-box as parent (the paragraph-pool-box is the only exception;
// and this one is handled elsewhere).
// Top and bottom margins are not applied to inline-elements.
final StaticBoxLayoutProperties blp = parent.getStaticBoxLayoutProperties();
final BoxDefinition bdef = parent.getBoxDefinition();
final long insetTop = (blp.getBorderTop() + bdef.getPaddingTop());
return (insetTop + parent.getCachedY());
}
else
{
// there's no parent .. Should not happen, shouldn't it?
return (0);
}
}
}