/*
* 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.util;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import org.pentaho.reporting.engine.classic.core.layout.model.BorderEdge;
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.PageAreaBox;
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.RenderableReplacedContent;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderableReplacedContentBox;
import org.pentaho.reporting.engine.classic.core.layout.model.context.BoxDefinition;
import org.pentaho.reporting.engine.classic.core.layout.process.ComputeStaticPropertiesProcessStep;
import org.pentaho.reporting.engine.classic.core.style.BorderStyle;
import org.pentaho.reporting.engine.classic.core.style.ElementStyleKeys;
import org.pentaho.reporting.engine.classic.core.style.StyleSheet;
import org.pentaho.reporting.engine.classic.core.util.ShapeDrawable;
import org.pentaho.reporting.engine.classic.core.util.StrokeUtility;
import org.pentaho.reporting.engine.classic.core.util.geom.StrictGeomUtility;
import org.pentaho.reporting.libraries.resourceloader.factory.drawable.DrawableWrapper;
/**
* Creation-Date: 16.07.2007, 13:42:43
*
* @author Thomas Morgner
*/
public class ProcessUtility
{
private ProcessUtility()
{
}
/**
* Returns the computed block-context width. This width is a content-size width - so it excludes paddings and borders.
* (See CSS3-BOX 4.2; http://www.w3.org/TR/css3-box/#containing)
*
* @param box the box for which the block-context width should be computed.
* @return the block context width.
*/
public static long computeBlockContextWidth(final RenderNode box)
{
final RenderBox parentBlockContext = box.getParent();
if (parentBlockContext == null)
{
// page cannot have borders ...
final int type = box.getNodeType();
if ((type & LayoutNodeTypes.MASK_BOX_PAGEAREA) == LayoutNodeTypes.MASK_BOX_PAGEAREA)
{
final PageAreaBox pageAreaBox = (PageAreaBox) box;
final LogicalPageBox pageBox = pageAreaBox.getLogicalPage();
if (pageBox == null)
{
return 0;
}
return pageBox.getPageWidth();
}
if ((type & LayoutNodeTypes.TYPE_BOX_LOGICALPAGE) == LayoutNodeTypes.TYPE_BOX_LOGICALPAGE)
{
final LogicalPageBox logicalPage = (LogicalPageBox) box;
return logicalPage.getPageWidth();
}
return 0;
}
// A row or inline-box never establishes a context on its own; it always transfers the parent's block-render context.
// The parent's computed width is used as block context ..
return parentBlockContext.getCachedWidth();
}
public static boolean isContent(final RenderBox element,
final boolean ellipseAsBackground,
final boolean shapesAsContent)
{
// For legacy reasons: A single ReplacedContent in a paragraph means, we may have a old-style border and
// background definition.
if (element.getNodeType() == LayoutNodeTypes.TYPE_BOX_CONTENT)
{
final RenderableReplacedContentBox contentBox = (RenderableReplacedContentBox) element;
final RenderableReplacedContent rpc = contentBox.getContent();
final Object rawContentObject = rpc.getRawObject();
if (rawContentObject instanceof DrawableWrapper == false)
{
return true;
}
final DrawableWrapper wrapper = (DrawableWrapper) rawContentObject;
final Object rawbackend = wrapper.getBackend();
if (rawbackend instanceof ShapeDrawable == false)
{
return true;
}
final ShapeDrawable drawable = (ShapeDrawable) rawbackend;
final Shape rawObject = drawable.getShape();
final StyleSheet styleSheet = element.getStyleSheet();
if (shapesAsContent == false)
{
return false;
}
if (rawObject instanceof Line2D)
{
if (hasBorderEdge(styleSheet))
{
final Line2D line = (Line2D) rawObject;
if (line.getY1() == line.getY2())
{
return false;
}
else if (line.getX1() == line.getX2())
{
return false;
}
}
}
else if (rawObject instanceof Rectangle2D)
{
return false;
}
else if (ellipseAsBackground && rawObject instanceof Ellipse2D)
{
return false;
}
else if (rawObject instanceof RoundRectangle2D)
{
return false;
}
return true;
}
RenderNode child = element.getFirstChild();
while (child != null)
{
final int type = child.getNodeType();
if ((type & LayoutNodeTypes.MASK_BOX_INLINE) == LayoutNodeTypes.MASK_BOX_INLINE)
{
return true;
}
if (type == LayoutNodeTypes.TYPE_NODE_TEXT)
{
return true;
}
child = child.getNext();
}
return false;
}
public static boolean hasBorderEdge(final StyleSheet style)
{
final Stroke s = (Stroke) style.getStyleProperty(ElementStyleKeys.STROKE);
if (s instanceof BasicStroke == false)
{
return false;
}
final BorderStyle borderStyle = StrokeUtility.translateStrokeStyle(s);
if (BorderStyle.NONE.equals(borderStyle))
{
return false;
}
return true;
}
public static BorderEdge produceBorderEdge(final StyleSheet style)
{
final Stroke s = (Stroke) style.getStyleProperty(ElementStyleKeys.STROKE);
if (s instanceof BasicStroke == false)
{
return null;
}
final BasicStroke bs = (BasicStroke) s;
final BorderStyle borderStyle = StrokeUtility.translateStrokeStyle(s);
if (BorderStyle.NONE.equals(borderStyle))
{
return null;
}
final Color c = (Color) style.getStyleProperty(ElementStyleKeys.PAINT);
return new BorderEdge(borderStyle, c, StrictGeomUtility.toInternalValue(bs.getLineWidth()));
}
public static long computeLength(final long min, final long max, final long pref)
{
if (pref > max)
{
if (max < min)
{
return min;
}
return max;
}
if (pref < min)
{
if (max < min)
{
return max;
}
return min;
}
if (max < pref)
{
return max;
}
return pref;
}
public static long resolveComputedWidth(final RenderBox box)
{
final long bcw = ProcessUtility.computeBlockContextWidth(box);
final BoxDefinition boxDef = box.getBoxDefinition();
final RenderLength minLength = boxDef.getMinimumWidth();
final RenderLength prefLength = boxDef.getPreferredWidth();
final RenderLength maxLength = boxDef.getMaximumWidth();
final long min = minLength.resolve(bcw, 0);
final long pref = prefLength.resolve(bcw, 0);
final long max = maxLength.resolve(bcw, ComputeStaticPropertiesProcessStep.MAX_AUTO);
return ProcessUtility.computeLength(min, max, pref);
}
}