/*
* 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;
}
}