/*
* 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.modules.output.pageable.graphics.internal;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import org.pentaho.reporting.engine.classic.core.layout.model.Border;
import org.pentaho.reporting.engine.classic.core.layout.model.BorderEdge;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderBox;
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.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.geom.StrictGeomUtility;
/**
* Creation-Date: 28.10.2007, 15:52:19
*
* @author Thomas Morgner
*/
public class BorderRenderer
{
private static final AffineTransform scaleInstance;
static
{
final long conversionFactor = StrictGeomUtility.toInternalValue(1);
scaleInstance = AffineTransform.getScaleInstance(1.0 / conversionFactor, 1.0 / conversionFactor);
}
private boolean sameForAllSides;
private Color backgroundColor;
private Shape borderShape;
private Shape borderShapeTop;
private Shape borderShapeLeft;
private Shape borderShapeBottom;
private Shape borderShapeRight;
private Arc2D reusableArc;
// private StyleSheet styleSheet;
private BoxDefinition boxDefinition;
private long x;
private long y;
private long width;
private long height;
private StaticBoxLayoutProperties staticBoxLayoutProperties;
public BorderRenderer()
{
reusableArc = new Arc2D.Double();
}
private void initialize(final RenderBox box)
{
initialize(box.getStaticBoxLayoutProperties(), box.getBoxDefinition(), box.getStyleSheet(),
box.getX(), box.getY(), box.getWidth(), box.getHeight());
}
private void initialize(final StaticBoxLayoutProperties staticBoxLayoutProperties,
final BoxDefinition boxDefinition,
final StyleSheet styleSheet,
final long x,
final long y,
final long width,
final long height)
{
this.staticBoxLayoutProperties = staticBoxLayoutProperties;
this.boxDefinition = boxDefinition;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.sameForAllSides = boxDefinition.getBorder().isSameForAllSides();
this.backgroundColor = (Color) styleSheet.getStyleProperty(ElementStyleKeys.BACKGROUND_COLOR);
this.borderShape = null;
this.borderShapeTop = null;
this.borderShapeLeft = null;
this.borderShapeBottom = null;
this.borderShapeRight = null;
}
private BasicStroke createStroke(final BorderEdge edge, final long internalWidth)
{
final float effectiveWidth = (float) StrictGeomUtility.toExternalValue(internalWidth);
if (BorderStyle.HIDDEN.equals(edge.getBorderStyle()))
{
return null;
}
if (BorderStyle.DASHED.equals(edge.getBorderStyle()))
{
return new BasicStroke(effectiveWidth, BasicStroke.CAP_SQUARE,
BasicStroke.JOIN_MITER,
10.0f, new float[]
{6 * effectiveWidth, 6 * effectiveWidth}, 0.0f);
}
if (BorderStyle.DOTTED.equals(edge.getBorderStyle()))
{
return new BasicStroke(effectiveWidth, BasicStroke.CAP_SQUARE,
BasicStroke.JOIN_MITER,
5.0f, new float[]{0.0f, 2 * effectiveWidth}, 0.0f);
}
if (BorderStyle.DOT_DASH.equals(edge.getBorderStyle()))
{
return new BasicStroke(effectiveWidth, BasicStroke.CAP_SQUARE,
BasicStroke.JOIN_MITER,
10.0f, new float[]
{0, 2 * effectiveWidth, 6 * effectiveWidth, 2 * effectiveWidth}, 0.0f);
}
if (BorderStyle.DOT_DOT_DASH.equals(edge.getBorderStyle()))
{
return new BasicStroke(effectiveWidth, BasicStroke.CAP_SQUARE,
BasicStroke.JOIN_MITER,
10.0f, new float[]{0, 2 * effectiveWidth,
0, 2 * effectiveWidth,
6 * effectiveWidth, 2 * effectiveWidth}, 0.0f);
}
return new BasicStroke(effectiveWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
}
public void paintBackgroundAndBorder(final RenderBox box, final Graphics2D g2d)
{
initialize(box);
paint(g2d);
}
public void paintBackgroundAndBorder(final StaticBoxLayoutProperties staticBoxLayoutProperties,
final BoxDefinition boxDefinition,
final StyleSheet styleSheet,
final long x,
final long y,
final long width,
final long height, final Graphics2D g2d)
{
initialize(staticBoxLayoutProperties, boxDefinition, styleSheet, x, y, width, height);
paint(g2d);
}
private void paint(final Graphics2D g2d)
{
final Border border = boxDefinition.getBorder();
if (backgroundColor == null && border.isEmpty())
{
return;
}
final Color oldColor = g2d.getColor();
final Stroke oldStroke = g2d.getStroke();
if (isSameForAllSides())
{
final Shape borderShape = getBorderShape();
if (backgroundColor != null)
{
g2d.setColor(backgroundColor);
g2d.fill(borderShape);
}
if (staticBoxLayoutProperties.getBorderTop() > 0)
{
final BorderEdge borderEdge = border.getTop();
final BasicStroke basicStroke = createStroke(borderEdge, staticBoxLayoutProperties.getBorderTop());
if (basicStroke != null)
{
g2d.setColor(borderEdge.getColor());
g2d.setStroke(basicStroke);
g2d.draw(borderShape);
}
}
g2d.setColor(oldColor);
g2d.setStroke(oldStroke);
return;
}
if (backgroundColor != null)
{
final Shape borderShape = getBorderShape();
g2d.setColor(backgroundColor);
g2d.fill(borderShape);
}
final StaticBoxLayoutProperties sblp = this.staticBoxLayoutProperties;
if (sblp.getBorderTop() > 0)
{
final BorderEdge borderEdge = border.getTop();
final BasicStroke basicStroke = createStroke(borderEdge, staticBoxLayoutProperties.getBorderTop());
if (basicStroke != null)
{
g2d.setColor(borderEdge.getColor());
g2d.setStroke(basicStroke);
g2d.draw(getBorderTopShape());
}
}
if (sblp.getBorderRight() > 0)
{
final BorderEdge borderEdge = border.getRight();
final BasicStroke basicStroke = createStroke(borderEdge, staticBoxLayoutProperties.getBorderRight());
if (basicStroke != null)
{
g2d.setColor(borderEdge.getColor());
g2d.setStroke(basicStroke);
g2d.draw(getBorderRightShape());
}
}
if (sblp.getBorderBottom() > 0)
{
final BorderEdge borderEdge = border.getBottom();
final BasicStroke basicStroke = createStroke(borderEdge, staticBoxLayoutProperties.getBorderBottom());
if (basicStroke != null)
{
g2d.setColor(borderEdge.getColor());
g2d.setStroke(basicStroke);
g2d.draw(getBorderBottomShape());
}
}
if (sblp.getBorderLeft() > 0)
{
final BorderEdge borderEdge = border.getLeft();
final BasicStroke basicStroke = createStroke(borderEdge, staticBoxLayoutProperties.getBorderLeft());
if (basicStroke != null)
{
g2d.setColor(borderEdge.getColor());
g2d.setStroke(basicStroke);
g2d.draw(getBorderLeftShape());
}
}
g2d.setColor(oldColor);
g2d.setStroke(oldStroke);
}
private boolean isSameForAllSides()
{
return sameForAllSides;
}
private Arc2D configureArc(final double x, final double y, final double w, final double h,
final double angSt, final double angExt, final int closure)
{
reusableArc.setArc(x, y, w, h, angSt, angExt, closure);
return reusableArc;
}
public Shape getBorderShape()
{
if (borderShape != null)
{
return borderShape;
}
final StaticBoxLayoutProperties sblp = this.staticBoxLayoutProperties;
final long x = this.x + (sblp.getBorderLeft() / 2);
final long y = this.y + (sblp.getBorderTop() / 2);
final long w = this.width - ((sblp.getBorderLeft() + sblp.getBorderRight()) / 2);
final long h = this.height - ((sblp.getBorderTop() + sblp.getBorderBottom()) / 2);
final Border border = boxDefinition.getBorder();
final long topLeftWidth = border.getTopLeft().getWidth();
final long topLeftHeight = border.getTopLeft().getHeight();
final long topRightWidth;
final long topRightHeight;
final long bottomLeftWidth;
final long bottomLeftHeight;
final long bottomRightWidth;
final long bottomRightHeight;
if (isSameForAllSides())
{
topRightWidth = topLeftWidth;
topRightHeight = topLeftHeight;
bottomLeftWidth = topLeftWidth;
bottomLeftHeight = topLeftHeight;
bottomRightWidth = topLeftWidth;
bottomRightHeight = topLeftHeight;
}
else
{
topRightWidth = border.getTopRight().getWidth();
topRightHeight = border.getTopRight().getHeight();
bottomLeftWidth = border.getBottomLeft().getWidth();
bottomLeftHeight = border.getBottomLeft().getHeight();
bottomRightWidth = border.getBottomRight().getWidth();
bottomRightHeight = border.getBottomRight().getHeight();
}
if (topLeftHeight == 0 && topRightHeight == 0 && topLeftWidth == 0 && topRightWidth == 0 &&
bottomLeftHeight == 0 && bottomRightHeight == 0 && bottomLeftWidth == 0 && bottomRightWidth == 0)
{
borderShape = new Rectangle2D.Double(StrictGeomUtility.toExternalValue(x), StrictGeomUtility.toExternalValue(y),
StrictGeomUtility.toExternalValue(w), StrictGeomUtility.toExternalValue(h));
return borderShape;
}
final GeneralPath generalPath = new GeneralPath(GeneralPath.WIND_NON_ZERO, 200);
generalPath.append(configureArc(x, y, 2 * topLeftWidth, 2 * topLeftHeight, -225, -45, Arc2D.OPEN), true);
generalPath.lineTo((float) (x + w - topRightWidth), (float) y);//2
generalPath.append(configureArc(x + w - 2 * topRightWidth, y, 2 * topRightWidth, 2 * topRightHeight, 90, -45,
Arc2D.OPEN), true);
generalPath.append(configureArc(x + w - 2 * topRightWidth, y, 2 * topRightWidth, 2 * topRightHeight, 45, -45,
Arc2D.OPEN), true);
generalPath.lineTo((float) (x + w), (float) (y + h - bottomRightHeight));//4
generalPath.append(configureArc(x + w - 2 * bottomRightWidth, y + h - 2 * bottomRightHeight, 2 * bottomRightWidth,
2 * bottomRightHeight, 0, -45, Arc2D.OPEN), true);
generalPath.append(configureArc(x + w - 2 * bottomRightWidth, y + h - 2 * bottomRightHeight, 2 * bottomRightWidth,
2 * bottomRightHeight, -45, -45, Arc2D.OPEN), true);
generalPath.lineTo((float) (x + bottomLeftWidth), (float) (y + h));//6
generalPath.append(configureArc(x, y + h - 2 * bottomLeftHeight, 2 * bottomLeftWidth, 2 * bottomLeftHeight, -90,
-45, Arc2D.OPEN), true);
generalPath.append(configureArc(x, y + h - 2 * bottomLeftHeight, 2 * bottomLeftWidth, 2 * bottomLeftHeight, -135,
-45, Arc2D.OPEN), true);
generalPath.lineTo((float) x, (float) (y + topLeftHeight));//8
generalPath.append(configureArc(x, y, 2 * topLeftWidth, 2 * topLeftHeight, -180, -45, Arc2D.OPEN), true);
generalPath.closePath();
generalPath.transform(BorderRenderer.scaleInstance);
borderShape = generalPath;
return generalPath;
}
public Shape getBorderTopShape()
{
if (borderShapeTop != null)
{
return borderShapeTop;
}
final StaticBoxLayoutProperties sblp = this.staticBoxLayoutProperties;
final long halfBorderWidth = sblp.getBorderTop() / 2;
final long x = this.x;
final long y = this.y + halfBorderWidth;
final long w = this.width;
final Border border = boxDefinition.getBorder();
final long topLeftWidth = border.getTopLeft().getWidth();
final long topLeftHeight = border.getTopLeft().getHeight();
final long topRightWidth = border.getTopRight().getWidth();
final long topRightHeight = border.getTopRight().getHeight();
if (topLeftWidth == 0 && topRightWidth == 0 && topLeftHeight == 0 && topRightHeight == 0)
{
// Make a square corner
final double lineX1 = StrictGeomUtility.toExternalValue(x);
final double lineX2 = StrictGeomUtility.toExternalValue(x + w);
final double lineY = StrictGeomUtility.toExternalValue(y);
borderShapeTop = new Line2D.Double(lineX1, lineY, lineX2, lineY);
return borderShapeTop;
}
// Make a rounded corner
final GeneralPath generalPath = new GeneralPath(GeneralPath.WIND_NON_ZERO, 20);
generalPath.append(configureArc(x, y, 2 * topLeftWidth, 2 * topLeftHeight, -225, -45, Arc2D.OPEN), true);
generalPath.lineTo((float) (x + w - topRightWidth), (float) y);//2
generalPath.append(configureArc(x + w - 2 * topRightWidth, y, 2 * topRightWidth, 2 * topRightHeight, 90, -45,
Arc2D.OPEN), true);
generalPath.transform(BorderRenderer.scaleInstance);
borderShapeTop = generalPath;
return generalPath;
}
public Shape getBorderBottomShape()
{
if (borderShapeBottom != null)
{
return borderShapeBottom;
}
final StaticBoxLayoutProperties sblp = this.staticBoxLayoutProperties;
final long halfBorderWidth = sblp.getBorderBottom() / 2;
final long x = this.x;
final long y = this.y;
final long w = this.width;
final long h = this.height;
final Border border = boxDefinition.getBorder();
final long bottomLeftWidth = border.getBottomLeft().getWidth();
final long bottomLeftHeight = border.getBottomLeft().getHeight();
final long bottomRightWidth = border.getBottomRight().getWidth();
final long bottomRightHeight = border.getBottomRight().getHeight();
if (bottomLeftWidth == 0 && bottomRightWidth == 0 && bottomLeftHeight == 0 && bottomRightHeight == 0)
{
// Make a square corner
final double lineX1 = StrictGeomUtility.toExternalValue(x);
final double lineX2 = StrictGeomUtility.toExternalValue(x + w);
final double lineY = StrictGeomUtility.toExternalValue(y + h - halfBorderWidth);
borderShapeBottom = new Line2D.Double(lineX1, lineY, lineX2, lineY);
return borderShapeBottom;
}
// Make a rounded corner
final GeneralPath generalPath = new GeneralPath(GeneralPath.WIND_NON_ZERO, 20);
generalPath.append(configureArc(x + w - 2 * bottomRightWidth, y + h - 2 * bottomRightHeight, 2 * bottomRightWidth,
2 * bottomRightHeight, -45, -45, Arc2D.OPEN), true);
generalPath.lineTo((float) (x + bottomLeftWidth), (float) (y + h));//6
generalPath.append(configureArc(x, y + h - 2 * bottomLeftHeight, 2 * bottomLeftWidth, 2 * bottomLeftHeight, -90,
-45, Arc2D.OPEN), true);
generalPath.transform(BorderRenderer.scaleInstance);
borderShapeBottom = generalPath;
return generalPath;
}
public Shape getBorderLeftShape()
{
if (borderShapeLeft != null)
{
return borderShapeLeft;
}
final StaticBoxLayoutProperties sblp = this.staticBoxLayoutProperties;
final long halfBorderWidth = sblp.getBorderLeft() / 2;
final long x = this.x;
final long y = this.y;
final long h = this.height;
final Border border = boxDefinition.getBorder();
final long topLeftWidth = border.getTopLeft().getWidth();
final long topLeftHeight = border.getTopLeft().getHeight();
final long bottomLeftWidth = border.getBottomLeft().getWidth();
final long bottomLeftHeight = border.getBottomLeft().getHeight();
if (bottomLeftWidth == 0 && topLeftWidth == 0 && bottomLeftHeight == 0 && topLeftHeight == 0)
{
// Make a square corner
final double lineX = StrictGeomUtility.toExternalValue(x + halfBorderWidth);
final double lineY1 = StrictGeomUtility.toExternalValue(y);
final double lineY2 = StrictGeomUtility.toExternalValue(y + h);
borderShapeLeft = new Line2D.Double(lineX, lineY1, lineX, lineY2);
return borderShapeLeft;
}
// Make a rounded corner
final GeneralPath generalPath = new GeneralPath(GeneralPath.WIND_NON_ZERO, 20);
generalPath.append(configureArc(x, y + h - 2 * bottomLeftHeight, 2 * bottomLeftWidth, 2 * bottomLeftHeight, -135,
-45, Arc2D.OPEN), true);
generalPath.lineTo((float) x, (float) (y + topLeftHeight));//8
generalPath.append(configureArc(x, y, 2 * topLeftWidth, 2 * topLeftHeight, -180, -45, Arc2D.OPEN), true);
generalPath.transform(BorderRenderer.scaleInstance);
borderShapeLeft = generalPath;
return generalPath;
}
public Shape getBorderRightShape()
{
if (borderShapeRight != null)
{
return borderShapeRight;
}
final StaticBoxLayoutProperties sblp = this.staticBoxLayoutProperties;
final long halfBorderWidth = sblp.getBorderRight() / 2;
final long x = this.x;
final long y = this.y;
final long w = this.width;
final long h = this.height;
final Border border = boxDefinition.getBorder();
final long topRightWidth = border.getTopRight().getWidth();
final long topRightHeight = border.getTopRight().getHeight();
final long bottomRightWidth = border.getBottomRight().getWidth();
final long bottomRightHeight = border.getBottomRight().getHeight();
if (topRightWidth == 0 && bottomRightWidth == 0 && topRightHeight == 0 && bottomRightHeight == 0)
{
// Make a square corner
final double lineX = StrictGeomUtility.toExternalValue(x + w - halfBorderWidth);
final double lineY1 = StrictGeomUtility.toExternalValue(y);
final double lineY2 = StrictGeomUtility.toExternalValue(y + h);
borderShapeRight = new Line2D.Double(lineX, lineY1, lineX, lineY2);
return borderShapeRight;
}
// Make a rounded corner
final GeneralPath generalPath = new GeneralPath(GeneralPath.WIND_NON_ZERO, 20);
generalPath.append(configureArc(x + w - 2 * topRightWidth, y, 2 * topRightWidth, 2 * topRightHeight, 45, -45,
Arc2D.OPEN), true);
generalPath.lineTo((float) (x + w), (float) (y + h - bottomRightHeight));//4
generalPath.append(configureArc(x + w - 2 * bottomRightWidth, y + h - 2 * bottomRightHeight, 2 * bottomRightWidth,
2 * bottomRightHeight, 0, -45, Arc2D.OPEN), true);
generalPath.transform(BorderRenderer.scaleInstance);
borderShapeRight = generalPath;
return generalPath;
}
}