/*
* 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 - 2013 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.layout.process;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot;
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.PageGrid;
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.layout.model.table.TableCellRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.table.TableRowRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.table.TableSectionRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorFeature;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorMetaData;
import org.pentaho.reporting.engine.classic.core.layout.process.util.CacheBoxShifter;
import org.pentaho.reporting.engine.classic.core.layout.process.util.ProcessUtility;
import org.pentaho.reporting.engine.classic.core.layout.process.util.ReplacedContentUtil;
import org.pentaho.reporting.engine.classic.core.style.ElementStyleKeys;
import org.pentaho.reporting.engine.classic.core.util.geom.StrictGeomUtility;
/**
* 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 AbstractMajorAxisLayoutStep {
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.MAX_AUTO;
private boolean paranoidChecks = true;
private RevalidateAllAxisLayoutStep revalidateAllAxisLayoutStep;
private PageGrid pageGrid;
private boolean complexText;
public CanvasMajorAxisLayoutStep() {
super( true );
revalidateAllAxisLayoutStep = new RevalidateAllAxisLayoutStep();
paranoidChecks =
"true".equals( ClassicEngineBoot.getInstance().getGlobalConfig().getConfigProperty(
"org.pentaho.reporting.engine.classic.core.layout.process.ParanoidChecks" ) );
}
public void compute( final LogicalPageBox pageBox ) {
try {
this.pageGrid = pageBox.getPageGrid();
super.compute( pageBox );
} finally {
this.pageGrid = null;
}
}
public void initialize( final OutputProcessorMetaData metaData ) {
revalidateAllAxisLayoutStep.initialize( metaData );
complexText = metaData.isFeatureSupported( OutputProcessorFeature.COMPLEX_TEXT );
}
private long resolveParentHeight( final RenderNode node ) {
final RenderBox parent = node.getParent();
if ( parent == null ) {
if ( node.getLayoutNodeType() == LayoutNodeTypes.TYPE_BOX_WATERMARK ) {
final LogicalPageBox box = node.getLogicalPage();
if ( box != null ) {
// a page-box has no margins, borders or paddings.
return box.getPageHeight();
}
}
return 0;
}
return Math.max( 0, parent.getCachedHeight() - parent.getVerticalInsets() );
}
protected boolean startBlockLevelBox( final RenderBox box ) {
if ( checkCacheValid( box ) ) {
return false;
}
final int strictNodeType = box.getNodeType();
performStartTable( box );
final long oldPosition = box.getCachedY();
final long newYPosition = computeVerticalBlockPosition( box );
CacheBoxShifter.shiftBox( box, Math.max( 0, newYPosition - oldPosition ) );
// Compute the block-position of the box. The box is positioned relative to the previous silbling or
// relative to the parent.
final int nodeType = box.getLayoutNodeType();
if ( nodeType == 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 {
long parentHeightForResolve = 0;
final RenderBox parent = box.getParent();
if ( parent != null && parent.getLayoutNodeType() == LayoutNodeTypes.TYPE_BOX_TABLE_CELL ) {
parentHeightForResolve = resolveParentHeight( box );
}
if ( ( nodeType & LayoutNodeTypes.MASK_BOX_BLOCK ) == LayoutNodeTypes.MASK_BOX_BLOCK ) {
final long blockHeight = computeBlockHeightAndAlign( box, parentHeightForResolve, false );
box.setCachedHeight( blockHeight );
} else if ( ( nodeType & LayoutNodeTypes.MASK_BOX_ROW ) == LayoutNodeTypes.MASK_BOX_ROW ) {
final long blockHeight = computeRowHeightAndAlign( box, parentHeightForResolve, false );
box.setCachedHeight( blockHeight );
} else if ( nodeType == LayoutNodeTypes.TYPE_BOX_CONTENT ) {
final RenderableReplacedContentBox rpc = (RenderableReplacedContentBox) box;
box.setCachedHeight( ReplacedContentUtil.computeHeight( rpc, parentHeightForResolve, box.getCachedWidth() ) );
} else {
final long cachedHeight = computeCanvasHeight( box, parentHeightForResolve == 0 );
box.setCachedHeight( cachedHeight );
}
}
}
return true;
}
private RenderBox isWatermark( final RenderBox box ) {
final RenderBox parent = box.getParent();
if ( parent == null ) {
return null;
}
if ( parent.getLayoutNodeType() == LayoutNodeTypes.TYPE_BOX_WATERMARK ) {
return parent;
}
final RenderBox parent2 = parent.getParent();
if ( parent2 == null ) {
return null;
}
if ( parent2.getLayoutNodeType() == 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 ( checkCacheValid( box ) ) {
return;
}
final int nodeType = box.getLayoutNodeType();
performFinishTable( box );
if ( nodeType == LayoutNodeTypes.TYPE_BOX_WATERMARK ) {
final WatermarkAreaBox watermarkAreaBox = (WatermarkAreaBox) box;
box.setCachedHeight( watermarkAreaBox.getLogicalPage().getPageHeight() );
} else {
long parentHeightForResolve = 0;
final RenderBox parent = box.getParent();
if ( parent != null && parent.getLayoutNodeType() == LayoutNodeTypes.TYPE_BOX_TABLE_CELL ) {
parentHeightForResolve = resolveParentHeight( box );
}
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, parentHeightForResolve, true );
box.setCachedHeight( blockHeight );
} else if ( ( nodeType & LayoutNodeTypes.MASK_BOX_ROW ) == LayoutNodeTypes.MASK_BOX_ROW ) {
final long blockHeight = computeRowHeightAndAlign( box, parentHeightForResolve, true );
box.setCachedHeight( blockHeight );
} else if ( nodeType == LayoutNodeTypes.TYPE_BOX_CONTENT ) {
// do nothing ..
return;
} else {
box.setCachedHeight( computeCanvasHeight( box, parentHeightForResolve == 0 ) );
}
}
}
private long computeVerticalBlockPosition( final RenderNode node ) {
return InfiniteMajorAxisLayoutStep.computeVerticalBlockPosition( node );
}
private long computeBlockHeightAndAlign( final RenderBox box, final long resolveSize, final boolean alignChilds ) {
return InfiniteMajorAxisLayoutStep.computeBlockHeightAndAlign( box, box.getBoxDefinition(), resolveSize,
alignChilds );
}
private long computeRowHeightAndAlign( final RenderBox box, final long resolveSize, final boolean align ) {
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.getLayoutNodeType() == 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 );
}
if ( align ) {
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 ) {
revalidateAllAxisLayoutStep.processBoxChilds( box, pageGrid );
}
protected boolean startCanvasLevelBox( final RenderBox box ) {
if ( checkCacheValid( box ) ) {
return false;
}
performStartTable( box );
final long oldPosition = box.getCachedY();
final long newYPosition = computeVerticalCanvasPosition( box );
CacheBoxShifter.shiftBox( box, Math.max( 0, newYPosition - oldPosition ) );
final int nodeType = box.getLayoutNodeType();
if ( ( nodeType & LayoutNodeTypes.MASK_BOX_BLOCK ) == LayoutNodeTypes.MASK_BOX_BLOCK ) {
final long resolveSize = resolveParentHeight( box );
final long blockHeight = computeBlockHeightAndAlign( box, resolveSize, false );
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, false );
box.setCachedHeight( blockHeight );
} else if ( nodeType == 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 ) );
}
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 ( checkCacheValid( box ) ) {
return;
}
final int type = box.getLayoutNodeType();
if ( type == LayoutNodeTypes.TYPE_BOX_TABLE ) {
performFinishTable( box );
box.setCachedHeight( computeCanvasHeight( box, false ) );
return;
}
if ( ( type & LayoutNodeTypes.MASK_BOX_BLOCK ) == LayoutNodeTypes.MASK_BOX_BLOCK ) {
final long resolveSize = resolveParentHeight( box );
final long blockHeight = computeBlockHeightAndAlign( box, resolveSize, true );
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, true );
box.setCachedHeight( blockHeight );
} else if ( type == LayoutNodeTypes.TYPE_BOX_CONTENT ) {
// do nothing ..
return;
} 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.getLayoutNodeType() & 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 ( paranoidChecks && 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 ) ) {
final long childConsumedHeight = parentAvailableHeight - node.getCachedHeight();
if ( childConsumedHeight < 0 ) {
if ( parent.getLayoutNodeType() == LayoutNodeTypes.TYPE_BOX_TABLE_CELL
|| parent.getLayoutNodeType() == LayoutNodeTypes.TYPE_BOX_TABLE_ROW ) {
// row-spanned cells consistently exceed the parent height ..
return 0;
}
logger.warn( "A child cannot exceed the area of the parent: " + node.getName() + " Parent: "
+ parentAvailableHeight + " Child: " + childConsumedHeight );
}
}
}
}
return parentAvailableHeight;
}
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.
final long minHeightResolved = minHeight.resolve( parentHeight );
final long maxHeightResolved = maxHeight.resolve( parentHeight, CanvasMajorAxisLayoutStep.MAX_AUTO );
if ( box.isSizeSpecifiesBorderBox() ) {
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 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 ( checkCacheValid( box ) ) {
return false;
}
performStartTable( box );
final long oldPosition = box.getCachedY();
final long newYPosition = computeVerticalRowPosition( box );
CacheBoxShifter.shiftBox( box, Math.max( 0, newYPosition - oldPosition ) );
// Compute the block-position of the box. The box is positioned relative to the previous silbling or
// relative to the parent.
final int nodeType = box.getLayoutNodeType();
if ( ( nodeType & LayoutNodeTypes.MASK_BOX_BLOCK ) == LayoutNodeTypes.MASK_BOX_BLOCK ) {
final long resolveSize = resolveParentHeight( box );
final long blockHeight = computeBlockHeightAndAlign( box, resolveSize, false );
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, false );
box.setCachedHeight( blockHeight );
} else if ( nodeType == 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 );
}
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 ( checkCacheValid( box ) ) {
return;
}
final int nodeType = box.getLayoutNodeType();
performFinishTable( box );
if ( ( nodeType & LayoutNodeTypes.MASK_BOX_BLOCK ) == LayoutNodeTypes.MASK_BOX_BLOCK ) {
final long resolveSize = resolveParentHeight( box );
final long blockHeight = computeBlockHeightAndAlign( box, resolveSize, true );
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, true );
box.setCachedHeight( blockHeight );
} else if ( nodeType == LayoutNodeTypes.TYPE_BOX_CONTENT ) {
// do nothing ..
return;
} else {
box.setCachedHeight( computeCanvasHeight( box, false ) );
}
}
private long computeVerticalRowPosition( final RenderNode node ) {
if ( node.isVisible() == false ) {
return node.getCachedY();
}
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 );
}
}
protected boolean startTableCellLevelBox( final RenderBox box ) {
return startBlockLevelBox( box );
}
protected void finishTableCellLevelBox( final RenderBox box ) {
// table cells behave like block-level cells most of the time.
finishBlockLevelBox( box );
}
protected boolean startTableRowLevelBox( final RenderBox box ) {
final long oldPosition = box.getCachedY();
final long newYPosition = computeVerticalRowPosition( box );
CacheBoxShifter.shiftBox( box, Math.max( 0, newYPosition - oldPosition ) );
if ( box.getNodeType() == LayoutNodeTypes.TYPE_BOX_TABLE_CELL ) {
getTableRowHeightStep().startTableCell( (TableCellRenderBox) box );
} else {
final long blockHeight = computeTableHeightAndAlign( box, false );
box.setCachedHeight( blockHeight );
}
markAllChildsDirty( box );
return true;
}
protected void finishTableRowLevelBox( final RenderBox box ) {
clearAllChildsDirtyMarker( box );
if ( box instanceof TableCellRenderBox ) {
final long blockHeight = computeTableHeightAndAlign( box, true );
getTableRowHeightStep().finishTableCell( (TableCellRenderBox) box, blockHeight );
} else {
final long blockHeight = computeTableHeightAndAlign( box, true );
box.setCachedHeight( blockHeight );
}
}
protected boolean startTableLevelBox( final RenderBox box ) {
final long oldPosition = box.getCachedY();
final long newYPosition = computeVerticalBlockPosition( box );
CacheBoxShifter.shiftBox( box, Math.max( 0, newYPosition - oldPosition ) );
final long blockHeight = computeTableHeightAndAlign( box, false );
box.setCachedHeight( blockHeight );
if ( box instanceof TableSectionRenderBox ) {
getTableRowHeightStep().startTableSection( (TableSectionRenderBox) box );
}
return true;
}
protected void processTableLevelNode( final RenderNode node ) {
processBlockLevelNode( node );
}
protected void finishTableLevelBox( final RenderBox box ) {
if ( box instanceof TableSectionRenderBox ) {
getTableRowHeightStep().finishTableSection( (TableSectionRenderBox) box );
} else {
final long blockHeight = computeTableHeightAndAlign( box, true );
box.setCachedHeight( blockHeight );
}
}
protected boolean startTableSectionLevelBox( final RenderBox box ) {
if ( box instanceof TableRowRenderBox ) {
getTableRowHeightStep().startTableRow( (TableRowRenderBox) box );
final long blockHeight = computeRowHeightAndAlign( box, 0, false );
box.setCachedHeight( blockHeight );
} else {
// must be an auto-box, so we treat it as a block-element.
final long oldPosition = box.getCachedY();
final long newYPosition = computeVerticalBlockPosition( box );
CacheBoxShifter.shiftBox( box, Math.max( 0, newYPosition - oldPosition ) );
final long blockHeight = computeTableHeightAndAlign( box, false );
box.setCachedHeight( blockHeight );
}
return true;
}
protected void processTableSectionLevelNode( final RenderNode node ) {
processBlockLevelNode( node );
}
protected void finishTableSectionLevelBox( final RenderBox box ) {
box.setCachedHeight( 0 );
}
private static long computeTableHeightAndAlign( final RenderBox box, final boolean align ) {
return InfiniteMajorAxisLayoutStep.computeBlockHeightAndAlign( box, BoxDefinition.EMPTY, 0, align );
}
}