/* * 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) 2006 - 2013 Pentaho Corporation.. All rights reserved. */ package org.pentaho.reporting.engine.classic.core.modules.output.table.html.helper; import java.awt.Color; import java.text.NumberFormat; import org.pentaho.reporting.engine.classic.core.ElementAlignment; import org.pentaho.reporting.engine.classic.core.layout.model.Border; import org.pentaho.reporting.engine.classic.core.layout.model.BorderCorner; import org.pentaho.reporting.engine.classic.core.layout.model.BorderEdge; import org.pentaho.reporting.engine.classic.core.layout.model.context.BoxDefinition; import org.pentaho.reporting.engine.classic.core.modules.output.table.base.CellBackground; import org.pentaho.reporting.engine.classic.core.modules.output.table.html.util.HtmlColors; import org.pentaho.reporting.engine.classic.core.modules.output.table.html.util.HtmlEncoderUtil; 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.style.TextStyleKeys; import org.pentaho.reporting.engine.classic.core.style.WhitespaceCollapse; import org.pentaho.reporting.engine.classic.core.util.geom.StrictGeomUtility; import org.pentaho.reporting.libraries.base.config.Configuration; @SuppressWarnings( "HardCodedStringLiteral" ) public class DefaultStyleBuilderFactory implements StyleBuilderFactory { private boolean safariLengthFix; private boolean useWhitespacePreWrap; private boolean enableRoundBorderCorner; public DefaultStyleBuilderFactory() { } public boolean isSafariLengthFix() { return safariLengthFix; } public boolean isUseWhitespacePreWrap() { return useWhitespacePreWrap; } public boolean isEnableRoundBorderCorner() { return enableRoundBorderCorner; } public StyleBuilder produceTextStyle( StyleBuilder styleBuilder, final StyleSheet styleSheet, final BoxDefinition boxDefinition, final boolean includeBorder, final StyleBuilder.StyleCarrier[] parentElementStyle ) { if ( styleSheet == null ) { throw new NullPointerException(); } if ( styleBuilder == null ) { styleBuilder = new DefaultStyleBuilder( this ); } else { styleBuilder.clear(); } final FilterStyleBuilder filterStyleBuilder = new FilterStyleBuilder( styleBuilder, parentElementStyle ); final NumberFormat pointConverter = filterStyleBuilder.getPointConverter(); final Color textColor = (Color) styleSheet.getStyleProperty( ElementStyleKeys.PAINT ); final Color backgroundColor = (Color) styleSheet.getStyleProperty( ElementStyleKeys.BACKGROUND_COLOR ); if ( backgroundColor != null ) { filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.BACKGROUND_COLOR, HtmlColors .getColorString( backgroundColor ) ); } if ( includeBorder ) { processBorder( styleSheet, boxDefinition, filterStyleBuilder, pointConverter ); } if ( textColor != null ) { filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.COLOR, HtmlColors.getColorString( textColor ) ); } processFontStyle( styleSheet, filterStyleBuilder, pointConverter ); processTextDecoration( styleSheet, filterStyleBuilder ); final ElementAlignment align = (ElementAlignment) styleSheet.getStyleProperty( ElementStyleKeys.ALIGNMENT ); filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.TEXT_ALIGN, translateHorizontalAlignment( align ) ); final double wordSpacing = styleSheet.getDoubleStyleProperty( TextStyleKeys.WORD_SPACING, 0 ); filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.WORD_SPACING, pointConverter .format( fixLengthForSafari( wordSpacing ) ), "pt" ); final double minLetterSpacing = styleSheet.getDoubleStyleProperty( TextStyleKeys.X_MIN_LETTER_SPACING, 0 ); final double maxLetterSpacing = styleSheet.getDoubleStyleProperty( TextStyleKeys.X_MAX_LETTER_SPACING, 0 ); filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.LETTER_SPACING, pointConverter .format( fixLengthForSafari( Math.min( minLetterSpacing, maxLetterSpacing ) ) ), "pt" ); processWhiteSpaceCollapse( styleSheet, filterStyleBuilder ); return styleBuilder; } private void processWhiteSpaceCollapse( final StyleSheet styleSheet, final FilterStyleBuilder filterStyleBuilder ) { final WhitespaceCollapse wsCollapse = (WhitespaceCollapse) styleSheet.getStyleProperty( TextStyleKeys.WHITE_SPACE_COLLAPSE ); if ( WhitespaceCollapse.PRESERVE.equals( wsCollapse ) ) { if ( useWhitespacePreWrap ) { // this style does not work for IE6 and IE7, but heck, in that case they just behave as if normal mode is // selected. In that case multiple spaces are collapsed into a single space. filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.WHITE_SPACE, "pre-wrap" ); } else { filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.WHITE_SPACE, "pre" ); } } else if ( WhitespaceCollapse.PRESERVE_BREAKS.equals( wsCollapse ) ) { filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.WHITE_SPACE, "nowrap" ); } else { // discard is handled on the layouter level already; // collapse is the normal way of handling whitespaces in the engine. filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.WHITE_SPACE, "normal" ); } } private void processFontStyle( final StyleSheet styleSheet, final FilterStyleBuilder filterStyleBuilder, final NumberFormat pointConverter ) { filterStyleBuilder.appendRaw( DefaultStyleBuilder.CSSKeys.FONT_FAMILY, translateFontFamily( styleSheet ) ); filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.FONT_SIZE, pointConverter .format( fixLengthForSafari( styleSheet.getDoubleStyleProperty( TextStyleKeys.FONTSIZE, 0 ) ) ), "pt" ); if ( styleSheet.getBooleanStyleProperty( TextStyleKeys.BOLD ) ) { filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.FONT_WEIGHT, "bold" ); } else { filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.FONT_WEIGHT, "normal" ); } if ( styleSheet.getBooleanStyleProperty( TextStyleKeys.ITALIC ) ) { filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.FONT_STYLE, "italic" ); } else { filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.FONT_STYLE, "normal" ); } } private void processTextDecoration( final StyleSheet styleSheet, final FilterStyleBuilder filterStyleBuilder ) { final boolean underlined = styleSheet.getBooleanStyleProperty( TextStyleKeys.UNDERLINED ); final boolean strikeThrough = styleSheet.getBooleanStyleProperty( TextStyleKeys.STRIKETHROUGH ); if ( underlined && strikeThrough ) { filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.TEXT_DECORATION, "underline line-through" ); } else if ( strikeThrough ) { filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.TEXT_DECORATION, "line-through" ); } if ( underlined ) { filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.TEXT_DECORATION, "underline" ); } else { filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.TEXT_DECORATION, "none" ); } } private void processBorder( final StyleSheet styleSheet, final BoxDefinition boxDefinition, final FilterStyleBuilder filterStyleBuilder, final NumberFormat pointConverter ) { final Border border = boxDefinition.getBorder(); final BorderEdge top = border.getTop(); final BorderEdge left = border.getLeft(); final BorderEdge bottom = border.getBottom(); final BorderEdge right = border.getRight(); if ( top.equals( left ) && top.equals( right ) && top.equals( bottom ) ) { if ( BorderEdge.EMPTY.equals( top ) == false ) { filterStyleBuilder.appendRaw( DefaultStyleBuilder.CSSKeys.BORDER, filterStyleBuilder.printEdgeAsCSS( top ) ); } } else { if ( top != null && BorderEdge.EMPTY.equals( top ) == false ) { filterStyleBuilder.appendRaw( DefaultStyleBuilder.CSSKeys.BORDER_TOP, filterStyleBuilder.printEdgeAsCSS( top ) ); } if ( left != null && BorderEdge.EMPTY.equals( left ) == false ) { filterStyleBuilder .appendRaw( DefaultStyleBuilder.CSSKeys.BORDER_LEFT, filterStyleBuilder.printEdgeAsCSS( left ) ); } if ( bottom != null && BorderEdge.EMPTY.equals( bottom ) == false ) { filterStyleBuilder.appendRaw( DefaultStyleBuilder.CSSKeys.BORDER_BOTTOM, filterStyleBuilder .printEdgeAsCSS( bottom ) ); } if ( right != null && BorderEdge.EMPTY.equals( right ) == false ) { filterStyleBuilder.appendRaw( DefaultStyleBuilder.CSSKeys.BORDER_RIGHT, filterStyleBuilder .printEdgeAsCSS( right ) ); } } if ( enableRoundBorderCorner ) { final double blW = Math.max( 0, styleSheet.getDoubleStyleProperty( ElementStyleKeys.BORDER_BOTTOM_LEFT_RADIUS_WIDTH, 0 ) ); final double blH = Math.max( 0, styleSheet.getDoubleStyleProperty( ElementStyleKeys.BORDER_BOTTOM_LEFT_RADIUS_HEIGHT, 0 ) ); if ( blW > 0 && blH > 0 ) { filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.MOZ_BORDER_RADIUS_BOTTOM_LEFT, pointConverter .format( fixLengthForSafari( blW ) ) + "pt " + pointConverter.format( fixLengthForSafari( blH ) ) + "pt" ); filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.BORDER_BOTTOM_LEFT_RADIUS, pointConverter .format( fixLengthForSafari( blW ) ) + "pt " + pointConverter.format( fixLengthForSafari( blH ) ) + "pt" ); } final double brW = Math.max( 0, styleSheet.getDoubleStyleProperty( ElementStyleKeys.BORDER_BOTTOM_RIGHT_RADIUS_WIDTH, 0 ) ); final double brH = Math.max( 0, styleSheet.getDoubleStyleProperty( ElementStyleKeys.BORDER_BOTTOM_RIGHT_RADIUS_HEIGHT, 0 ) ); if ( brW > 0 && brH > 0 ) { filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.MOZ_BORDER_RADIUS_BOTTOM_RIGHT, pointConverter .format( fixLengthForSafari( brW ) ) + "pt " + pointConverter.format( fixLengthForSafari( brH ) ) + "pt" ); filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.BORDER_BOTTOM_RIGHT_RADIUS, pointConverter .format( fixLengthForSafari( brW ) ) + "pt " + pointConverter.format( fixLengthForSafari( brH ) ) + "pt" ); } final double tlW = Math.max( 0, styleSheet.getDoubleStyleProperty( ElementStyleKeys.BORDER_TOP_LEFT_RADIUS_WIDTH, 0 ) ); final double tlH = Math.max( 0, styleSheet.getDoubleStyleProperty( ElementStyleKeys.BORDER_TOP_LEFT_RADIUS_HEIGHT, 0 ) ); if ( tlW > 0 && tlH > 0 ) { filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.MOZ_BORDER_RADIUS_TOP_LEFT, pointConverter .format( fixLengthForSafari( tlW ) ) + "pt " + pointConverter.format( fixLengthForSafari( tlH ) ) + "pt" ); filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.BORDER_TOP_LEFT_RADIUS, pointConverter .format( fixLengthForSafari( tlW ) ) + "pt " + pointConverter.format( fixLengthForSafari( tlH ) ) + "pt" ); } final double trW = Math.max( 0, styleSheet.getDoubleStyleProperty( ElementStyleKeys.BORDER_TOP_RIGHT_RADIUS_WIDTH, 0 ) ); final double trH = Math.max( 0, styleSheet.getDoubleStyleProperty( ElementStyleKeys.BORDER_TOP_RIGHT_RADIUS_HEIGHT, 0 ) ); if ( trW > 0 && trH > 0 ) { filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.MOZ_BORDER_RADIUS_TOP_RIGHT, pointConverter .format( fixLengthForSafari( trW ) ) + "pt " + pointConverter.format( fixLengthForSafari( trH ) ) + "pt" ); filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.BORDER_TOP_RIGHT_RADIUS, pointConverter .format( fixLengthForSafari( trW ) ) + "pt " + pointConverter.format( fixLengthForSafari( trH ) ) + "pt" ); } } final long paddingTop = boxDefinition.getPaddingTop(); final long paddingLeft = boxDefinition.getPaddingLeft(); final long paddingBottom = boxDefinition.getPaddingBottom(); final long paddingRight = boxDefinition.getPaddingRight(); if ( paddingTop == paddingLeft && paddingTop == paddingRight && paddingTop == paddingBottom ) { if ( paddingTop > 0 ) { filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.PADDING, pointConverter .format( fixLengthForSafari( StrictGeomUtility.toExternalValue( paddingTop ) ) ), "pt" ); } } else { if ( paddingTop > 0 ) { filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.PADDING_TOP, pointConverter .format( fixLengthForSafari( StrictGeomUtility.toExternalValue( paddingTop ) ) ), "pt" ); } if ( paddingLeft > 0 ) { filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.PADDING_LEFT, pointConverter .format( fixLengthForSafari( StrictGeomUtility.toExternalValue( paddingLeft ) ) ), "pt" ); } if ( paddingBottom > 0 ) { filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.PADDING_BOTTOM, pointConverter .format( fixLengthForSafari( StrictGeomUtility.toExternalValue( paddingBottom ) ) ), "pt" ); } if ( paddingRight > 0 ) { filterStyleBuilder.append( DefaultStyleBuilder.CSSKeys.PADDING_RIGHT, pointConverter .format( fixLengthForSafari( StrictGeomUtility.toExternalValue( paddingRight ) ) ), "pt" ); } } } private static String translateFontFamily( final StyleSheet box ) { final String family = (String) box.getStyleProperty( TextStyleKeys.FONT ); if ( "Serif".equalsIgnoreCase( family ) ) { return "serif"; } else if ( "Sans-serif".equalsIgnoreCase( family ) || "SanSerif".equalsIgnoreCase( family ) || "SansSerif".equalsIgnoreCase( family ) || "Dialog".equalsIgnoreCase( family ) || "DialogInput".equalsIgnoreCase( family ) ) { return "sans-serif"; } else if ( "Monospaced".equalsIgnoreCase( family ) ) { return "monospace"; } else { return '\"' + HtmlEncoderUtil.encodeCSS( family ) + '\"'; } } public double fixLengthForSafari( final double border ) { if ( safariLengthFix == false ) { return border; } if ( border == 0 ) { return 0; } return Math.max( 1, Math.round( border ) ); } /** * Translates the JFreeReport horizontal element alignment into a HTML alignment constant. * * @param ea * the element alignment * @return the translated alignment name. */ public static String translateHorizontalAlignment( final ElementAlignment ea ) { if ( ElementAlignment.JUSTIFY.equals( ea ) ) { return "justify"; } if ( ElementAlignment.RIGHT.equals( ea ) ) { return "right"; } if ( ElementAlignment.CENTER.equals( ea ) ) { return "center"; } return "left"; } public void configure( final Configuration configuration ) { safariLengthFix = ( "true" .equals( configuration .getConfigProperty( "org.pentaho.reporting.engine.classic.core.modules.output.table.html.SafariLengthHack" ) ) ); useWhitespacePreWrap = ( "true" .equals( configuration .getConfigProperty( "org.pentaho.reporting.engine.classic.core.modules.output.table.html.UseWhitespacePreWrap" ) ) ); enableRoundBorderCorner = ( "true" .equals( configuration .getConfigProperty( "org.pentaho.reporting.engine.classic.core.modules.output.table.html.EnableRoundBorderCorner" ) ) ); } public StyleBuilder createCellStyle( StyleBuilder styleBuilder, final CellBackground background, final StyleBuilder.CSSKeys[] extraStyleKeys, final String[] extraStyleValues ) { return createCellStyle( styleBuilder, null, null, background, extraStyleKeys, extraStyleValues ); } public StyleBuilder createCellStyle( StyleBuilder styleBuilder, final StyleSheet styleSheet, final BoxDefinition boxDefinition, final CellBackground background, final StyleBuilder.CSSKeys[] extraStyleKeys, final String[] extraStyleValues ) { if ( styleBuilder == null ) { throw new NullPointerException(); } if ( styleSheet == null ) { styleBuilder.clear(); } else { styleBuilder = produceTextStyle( styleBuilder, styleSheet, boxDefinition, true, null ); } // Add the extra styles if ( extraStyleKeys != null && extraStyleValues != null && extraStyleKeys.length == extraStyleValues.length ) { for ( int i = 0; i < extraStyleKeys.length; ++i ) { styleBuilder.append( extraStyleKeys[i], extraStyleValues[i], false ); } } if ( background != null ) { final Color colorValue = ( background.getBackgroundColor() ); if ( colorValue != null ) { styleBuilder.append( DefaultStyleBuilder.CSSKeys.BACKGROUND_COLOR, HtmlColors.getColorString( colorValue ) ); } final BorderEdge topEdge = background.getTop(); final BorderEdge leftEdge = background.getLeft(); final BorderEdge bottomEdge = background.getBottom(); final BorderEdge rightEdge = background.getRight(); if ( topEdge.equals( leftEdge ) && topEdge.equals( rightEdge ) && topEdge.equals( bottomEdge ) ) { if ( BorderEdge.EMPTY.equals( topEdge ) == false ) { styleBuilder.appendRaw( DefaultStyleBuilder.CSSKeys.BORDER, styleBuilder.printEdgeAsCSS( topEdge ) ); } } else { if ( BorderEdge.EMPTY.equals( topEdge ) == false ) { styleBuilder.appendRaw( DefaultStyleBuilder.CSSKeys.BORDER_TOP, styleBuilder.printEdgeAsCSS( topEdge ) ); } if ( BorderEdge.EMPTY.equals( leftEdge ) == false ) { styleBuilder.appendRaw( DefaultStyleBuilder.CSSKeys.BORDER_LEFT, styleBuilder.printEdgeAsCSS( leftEdge ) ); } if ( BorderEdge.EMPTY.equals( bottomEdge ) == false ) { styleBuilder.appendRaw( DefaultStyleBuilder.CSSKeys.BORDER_BOTTOM, styleBuilder.printEdgeAsCSS( bottomEdge ) ); } if ( BorderEdge.EMPTY.equals( rightEdge ) == false ) { styleBuilder.appendRaw( DefaultStyleBuilder.CSSKeys.BORDER_RIGHT, styleBuilder.printEdgeAsCSS( rightEdge ) ); } } if ( isEnableRoundBorderCorner() ) { final BorderCorner topLeft = background.getTopLeft(); if ( BorderCorner.EMPTY.equals( topLeft ) == false ) { styleBuilder.append( DefaultStyleBuilder.CSSKeys.MOZ_BORDER_RADIUS_TOP_LEFT, styleBuilder .printCornerAsCSS( topLeft ) ); styleBuilder.append( DefaultStyleBuilder.CSSKeys.BORDER_TOP_LEFT_RADIUS, styleBuilder .printCornerAsCSS( topLeft ) ); } final BorderCorner topRight = background.getTopRight(); if ( BorderCorner.EMPTY.equals( topRight ) == false ) { styleBuilder.append( DefaultStyleBuilder.CSSKeys.MOZ_BORDER_RADIUS_TOP_RIGHT, styleBuilder .printCornerAsCSS( topRight ) ); styleBuilder.append( DefaultStyleBuilder.CSSKeys.BORDER_TOP_RIGHT_RADIUS, styleBuilder .printCornerAsCSS( topRight ) ); } final BorderCorner bottomLeft = background.getBottomLeft(); if ( BorderCorner.EMPTY.equals( bottomLeft ) == false ) { styleBuilder.append( DefaultStyleBuilder.CSSKeys.MOZ_BORDER_RADIUS_BOTTOM_LEFT, styleBuilder .printCornerAsCSS( bottomLeft ) ); styleBuilder.append( DefaultStyleBuilder.CSSKeys.BORDER_BOTTOM_LEFT_RADIUS, styleBuilder .printCornerAsCSS( bottomLeft ) ); } final BorderCorner bottomRight = background.getBottomRight(); if ( BorderCorner.EMPTY.equals( bottomRight ) == false ) { styleBuilder.append( DefaultStyleBuilder.CSSKeys.MOZ_BORDER_RADIUS_BOTTOM_RIGHT, styleBuilder .printCornerAsCSS( bottomRight ) ); styleBuilder.append( DefaultStyleBuilder.CSSKeys.BORDER_BOTTOM_RIGHT_RADIUS, styleBuilder .printCornerAsCSS( bottomRight ) ); } } } return styleBuilder; } }