/*! * 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) 2002-2017 Pentaho Corporation.. All rights reserved. */ package org.pentaho.reporting.engine.classic.core.layout.richtext; import java.awt.Color; import java.awt.Font; import java.io.IOException; import java.io.StreamTokenizer; import java.io.StringReader; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.MutableAttributeSet; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.html.CSS; import javax.swing.text.html.HTML; import javax.swing.text.html.HTMLDocument; import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.StyleSheet; import org.pentaho.reporting.engine.classic.core.AttributeNames; import org.pentaho.reporting.engine.classic.core.Band; import org.pentaho.reporting.engine.classic.core.Element; import org.pentaho.reporting.engine.classic.core.ReportElement; import org.pentaho.reporting.engine.classic.core.filter.types.ContentType; import org.pentaho.reporting.engine.classic.core.filter.types.LabelType; import org.pentaho.reporting.engine.classic.core.modules.parser.base.ReportParserUtil; import org.pentaho.reporting.engine.classic.core.style.BandStyleKeys; 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.StyleKey; import org.pentaho.reporting.engine.classic.core.style.TextStyleKeys; import org.pentaho.reporting.engine.classic.core.style.TextWrap; import org.pentaho.reporting.engine.classic.core.style.VerticalTextAlign; import org.pentaho.reporting.engine.classic.core.style.WhitespaceCollapse; import org.pentaho.reporting.libraries.base.util.StringUtils; import org.pentaho.reporting.libraries.xmlns.parser.ParseException; /** * This handles HTML 3.2 with some CSS support. It uses the Swing HTML parser to process the document. * * @author Thomas Morgner. */ public class HtmlRichTextConverter implements RichTextConverter { private HTMLEditorKit editorKit; private static final Set BLOCK_ELEMENTS; static { final HashSet<HTML.Tag> blockElements = new HashSet<HTML.Tag>(); blockElements.add( HTML.Tag.IMPLIED ); blockElements.add( HTML.Tag.APPLET ); blockElements.add( HTML.Tag.BODY ); blockElements.add( HTML.Tag.BLOCKQUOTE ); blockElements.add( HTML.Tag.DIV ); blockElements.add( HTML.Tag.FORM ); blockElements.add( HTML.Tag.FRAME ); blockElements.add( HTML.Tag.FRAMESET ); blockElements.add( HTML.Tag.H1 ); blockElements.add( HTML.Tag.H2 ); blockElements.add( HTML.Tag.H3 ); blockElements.add( HTML.Tag.H4 ); blockElements.add( HTML.Tag.H5 ); blockElements.add( HTML.Tag.H6 ); blockElements.add( HTML.Tag.HR ); blockElements.add( HTML.Tag.HTML ); blockElements.add( HTML.Tag.LI ); blockElements.add( HTML.Tag.NOFRAMES ); blockElements.add( HTML.Tag.OBJECT ); blockElements.add( HTML.Tag.OL ); blockElements.add( HTML.Tag.P ); blockElements.add( HTML.Tag.PRE ); blockElements.add( HTML.Tag.TABLE ); blockElements.add( HTML.Tag.TR ); blockElements.add( HTML.Tag.UL ); BLOCK_ELEMENTS = Collections.unmodifiableSet( blockElements ); } public HtmlRichTextConverter() { editorKit = new HTMLEditorKit(); } public boolean isRecognizedType( final String mimeType ) { if ( "text/html".equals( mimeType ) ) { return true; } return false; } public Object convert( final ReportElement source, final Object value ) { try { final Document doc = RichTextConverterUtilities.parseDocument( editorKit, value ); if ( doc == null ) { return value; } final Element element = process( doc.getDefaultRootElement(), null ); return RichTextConverterUtilities.convertToBand( StyleKey.getDefinedStyleKeysList(), source, element ); } catch ( Exception e ) { return value; } } private static AttributeSet computeStyle( final javax.swing.text.Element elem, final StyleSheet styles ) { final AttributeSet a = elem.getAttributes(); final AttributeSet htmlAttr = styles.translateHTMLToCSS( a ); final ArrayList<AttributeSet> muxList = new ArrayList<AttributeSet>(); if ( htmlAttr.getAttributeCount() != 0 ) { muxList.add( htmlAttr ); } if ( elem.isLeaf() ) { // The swing-parser has a very weird way of storing attributes for the HTML elements. The // tag-name is used as key for the attribute set, so you have to know the element type before // you can do anything sensible with it. Or as we do here, you have to search for the HTML.Tag // object. Arghh. final Enumeration keys = a.getAttributeNames(); while ( keys.hasMoreElements() ) { final Object key = keys.nextElement(); if ( !( key instanceof HTML.Tag ) ) { continue; } if ( key == HTML.Tag.A ) { final Object o = a.getAttribute( key ); if ( o instanceof AttributeSet ) { final AttributeSet attr = (AttributeSet) o; if ( attr.getAttribute( HTML.Attribute.HREF ) == null ) { continue; } else { SimpleAttributeSet hrefAttributeSet = new SimpleAttributeSet(); hrefAttributeSet.addAttribute( HTML.Attribute.HREF, attr.getAttribute( HTML.Attribute.HREF ) ); muxList.add( hrefAttributeSet ); } } } final AttributeSet cssRule = styles.getRule( (HTML.Tag) key, elem ); if ( cssRule != null ) { muxList.add( cssRule ); } } } else { final HTML.Tag t = (HTML.Tag) a.getAttribute( StyleConstants.NameAttribute ); final AttributeSet cssRule = styles.getRule( t, elem ); if ( cssRule != null ) { muxList.add( cssRule ); } } final MutableAttributeSet retval = new SimpleAttributeSet(); for ( int i = muxList.size() - 1; i >= 0; i-- ) { final AttributeSet o = muxList.get( i ); retval.addAttributes( o ); } return retval; } private Object convertURL( final String srcAttr ) { try { return new URL( srcAttr ); } catch ( MalformedURLException e ) { // ignore .. return srcAttr; } } private Element process( final javax.swing.text.Element textElement, final String liNum ) throws BadLocationException { if ( isInvisible( textElement ) ) { return null; } if ( textElement.isLeaf() ) { final AttributeSet attributes = textElement.getAttributes(); if ( HTML.Tag.IMG.equals( attributes.getAttribute( StyleConstants.NameAttribute ) ) ) { final Element result = new Element(); result.setName( textElement.getName() ); result.setElementType( new ContentType() ); final String src = (String) attributes.getAttribute( HTML.Attribute.SRC ); final String alt = (String) attributes.getAttribute( HTML.Attribute.TITLE ); result.setAttribute( AttributeNames.Core.NAMESPACE, AttributeNames.Core.VALUE, convertURL( src ) ); result.setAttribute( AttributeNames.Html.NAMESPACE, AttributeNames.Html.TITLE, alt ); result.setAttribute( AttributeNames.Html.NAMESPACE, AttributeNames.Swing.TOOLTIP, alt ); if ( attributes.isDefined( HTML.Attribute.WIDTH ) && attributes.isDefined( HTML.Attribute.HEIGHT ) ) { result.getStyle().setStyleProperty( ElementStyleKeys.SCALE, Boolean.TRUE ); result.getStyle().setStyleProperty( ElementStyleKeys.KEEP_ASPECT_RATIO, Boolean.FALSE ); result.getStyle().setStyleProperty( ElementStyleKeys.MIN_WIDTH, parseLength( String.valueOf( attributes.getAttribute( HTML.Attribute.WIDTH ) ) ) ); result.getStyle().setStyleProperty( ElementStyleKeys.MIN_HEIGHT, parseLength( String.valueOf( attributes.getAttribute( HTML.Attribute.HEIGHT ) ) ) ); } else if ( attributes.isDefined( HTML.Attribute.WIDTH ) ) { result.getStyle().setStyleProperty( ElementStyleKeys.SCALE, Boolean.TRUE ); result.getStyle().setStyleProperty( ElementStyleKeys.KEEP_ASPECT_RATIO, Boolean.TRUE ); result.getStyle().setStyleProperty( ElementStyleKeys.MIN_WIDTH, parseLength( String.valueOf( attributes.getAttribute( HTML.Attribute.WIDTH ) ) ) ); result.getStyle().setStyleProperty( ElementStyleKeys.DYNAMIC_HEIGHT, Boolean.TRUE ); } else if ( attributes.isDefined( HTML.Attribute.HEIGHT ) ) { result.getStyle().setStyleProperty( ElementStyleKeys.SCALE, Boolean.TRUE ); result.getStyle().setStyleProperty( ElementStyleKeys.KEEP_ASPECT_RATIO, Boolean.TRUE ); result.getStyle().setStyleProperty( ElementStyleKeys.MIN_HEIGHT, parseLength( String.valueOf( attributes.getAttribute( HTML.Attribute.HEIGHT ) ) ) ); result.getStyle().setStyleProperty( ElementStyleKeys.DYNAMIC_HEIGHT, Boolean.TRUE ); } else { result.getStyle().setStyleProperty( ElementStyleKeys.SCALE, Boolean.FALSE ); result.getStyle().setStyleProperty( ElementStyleKeys.KEEP_ASPECT_RATIO, Boolean.TRUE ); result.getStyle().setStyleProperty( ElementStyleKeys.DYNAMIC_HEIGHT, Boolean.TRUE ); } configureStyle( textElement, result ); return result; } final javax.swing.text.Element parent = textElement.getParentElement(); final int endOffset = textElement.getEndOffset(); final int startOffset = textElement.getStartOffset(); final String text = textElement.getDocument().getText( startOffset, endOffset - startOffset ); if ( parent != null ) { final HTML.Tag tag = findTag( parent.getAttributes() ); if ( "\n".equals( text ) ) { if ( BLOCK_ELEMENTS.contains( tag ) || "paragraph".equals( textElement.getName() ) || "section".equals( textElement.getName() ) ) { if ( parent.getElementCount() > 0 && parent.getElement( parent.getElementCount() - 1 ) == textElement ) { // Skipping an artificial \n at the end of paragraph element. This is generated by the swing // parser and really messes things up here. return null; } } } } final Element result = new Element(); result.setName( textElement.getName() ); result.setElementType( LabelType.INSTANCE ); result.setAttribute( AttributeNames.Core.NAMESPACE, AttributeNames.Core.VALUE, text ); configureStyle( textElement, result ); if ( HTML.Tag.BR.equals( textElement.getAttributes().getAttribute( StyleConstants.NameAttribute ) ) ) { result.setAttribute( AttributeNames.Core.NAMESPACE, AttributeNames.Core.VALUE, "\n" ); result.getStyle().setStyleProperty( TextStyleKeys.TRIM_TEXT_CONTENT, Boolean.FALSE ); result.getStyle().setStyleProperty( TextStyleKeys.WHITE_SPACE_COLLAPSE, WhitespaceCollapse.PRESERVE ); } return result; } // we need to intercept for <UL> and <OL> here final Band band = new Band(); configureStyle( textElement, band ); configureBand( textElement, band ); final boolean bandIsInline = isInlineElement( band ); final int size = textElement.getElementCount(); Band inlineContainer = null; for ( int i = 0; i < size; i++ ) { String listSign = liNum; if ( HTML.Tag.OL.equals( textElement.getAttributes().getAttribute( StyleConstants.NameAttribute ) ) ) { listSign = Integer.toString( i + 1 ) + ". "; } if ( HTML.Tag.UL.equals( textElement.getAttributes().getAttribute( StyleConstants.NameAttribute ) ) ) { listSign = "\u00B7"; } final Element element = process( textElement.getElement( i ), listSign ); if ( element == null ) { continue; } if ( "li".equals( textElement.getElement( i ).getName() ) ) { band.getStyle().setStyleProperty( BandStyleKeys.LAYOUT, "block" ); Band elemlistband = new Band(); elemlistband.getStyle().setStyleProperty( BandStyleKeys.LAYOUT, "block" ); elemlistband.getStyle().setStyleProperty( ElementStyleKeys.PADDING_LEFT, 10f ); elemlistband.addElement( element ); band.addElement( elemlistband ); continue; } if ( isInlineElement( element ) == bandIsInline ) { if ( "li".equals( textElement.getName() ) ) { if ( textElement.getElementCount() == 1 && ( ( HTML.Tag.OL.equals( textElement.getElement( 0 ).getAttributes().getAttribute( StyleConstants.NameAttribute ) ) || ( HTML.Tag.UL.equals( textElement.getElement( 0 ).getAttributes().getAttribute( StyleConstants.NameAttribute ) ) ) ) ) ) { band.addElement( createLiNumElement( liNum ) ); } } band.addElement( element ); continue; } if ( band.getElementCount() == 0 ) { inlineContainer = new Band(); inlineContainer.getStyle().setStyleProperty( BandStyleKeys.LAYOUT, "inline" ); if ( "li".equals( textElement.getParentElement().getName() ) ) { inlineContainer.addElement( createLiNumElement( liNum ) ); } inlineContainer.addElement( element ); band.addElement( inlineContainer ); continue; } final Element maybeInlineContainer = (Element) band.getElement( band.getElementCount() - 1 ); if ( maybeInlineContainer == inlineContainer ) { // InlineContainer cannot be null at this point, as band.getElement never returns null. // noinspection ConstantConditions inlineContainer.addElement( element ); continue; } inlineContainer = new Band(); inlineContainer.getStyle().setStyleProperty( BandStyleKeys.LAYOUT, "inline" ); inlineContainer.addElement( element ); band.addElement( inlineContainer ); } return band; } private Element createLiNumElement( final String _liNum ) { final Element linum = new Element(); linum.setName( "point" ); linum.setElementType( LabelType.INSTANCE ); linum.setAttribute( AttributeNames.Core.NAMESPACE, AttributeNames.Core.VALUE, _liNum ); return linum; } private boolean isInlineElement( final Element element ) { if ( element instanceof Band ) { if ( "inline".equals( element.getStyle().getStyleProperty( BandStyleKeys.LAYOUT, "inline" ) ) ) { return true; } return false; } return true; } private boolean isInvisible( final javax.swing.text.Element textElement ) { final HTMLDocument htmlDocument = (HTMLDocument) textElement.getDocument(); final StyleSheet sheet = htmlDocument.getStyleSheet(); final AttributeSet attr = computeStyle( textElement, sheet ); final Object o = attr.getAttribute( CSS.Attribute.DISPLAY ); if ( "none".equals( String.valueOf( o ) ) ) { return true; } final Object tag = findTag( textElement.getAttributes() ); if ( tag == HTML.Tag.COMMENT ) { return true; } if ( tag == HTML.Tag.SCRIPT ) { return true; } if ( tag == HTML.Tag.HEAD ) { return true; } return false; } private void configureStyle( final javax.swing.text.Element textElement, final Element result ) { final HTMLDocument htmlDocument = (HTMLDocument) textElement.getDocument(); final StyleSheet sheet = htmlDocument.getStyleSheet(); final AttributeSet attr = computeStyle( textElement, sheet ); parseBorderAndBackgroundStyle( result, sheet, attr ); parseBoxStyle( result, attr ); final Font font = sheet.getFont( attr ); if ( font != null ) { result.getStyle().setStyleProperty( TextStyleKeys.FONT, font.getFamily() ); result.getStyle().setStyleProperty( TextStyleKeys.FONTSIZE, font.getSize() ); result.getStyle().setBooleanStyleProperty( TextStyleKeys.ITALIC, font.isItalic() ); result.getStyle().setBooleanStyleProperty( TextStyleKeys.BOLD, font.isBold() ); } final Object letterSpacing = attr.getAttribute( CSS.Attribute.LETTER_SPACING ); if ( letterSpacing != null ) { result.getStyle().setStyleProperty( TextStyleKeys.X_OPTIMUM_LETTER_SPACING, parseLength( String.valueOf( letterSpacing ) ) ); } final Object wordSpacing = attr.getAttribute( CSS.Attribute.WORD_SPACING ); if ( wordSpacing != null ) { result.getStyle().setStyleProperty( TextStyleKeys.WORD_SPACING, parseLength( String.valueOf( wordSpacing ) ) ); } final Object lineHeight = attr.getAttribute( CSS.Attribute.LINE_HEIGHT ); if ( lineHeight != null ) { result.getStyle().setStyleProperty( TextStyleKeys.LINEHEIGHT, parseLength( String.valueOf( lineHeight ) ) ); } final Object textAlign = attr.getAttribute( CSS.Attribute.TEXT_ALIGN ); if ( textAlign != null ) { try { result.getStyle().setStyleProperty( ElementStyleKeys.ALIGNMENT, ReportParserUtil.parseHorizontalElementAlignment( String.valueOf( textAlign ), null ) ); } catch ( ParseException e ) { // ignore .. } } final Object textDecoration = attr.getAttribute( CSS.Attribute.TEXT_DECORATION ); if ( textDecoration != null ) { final String[] strings = StringUtils.split( String.valueOf( textDecoration ) ); result.getStyle().setStyleProperty( TextStyleKeys.STRIKETHROUGH, Boolean.FALSE ); result.getStyle().setStyleProperty( TextStyleKeys.UNDERLINED, Boolean.FALSE ); for ( int i = 0; i < strings.length; i++ ) { final String value = strings[i]; if ( "line-through".equals( value ) ) { result.getStyle().setStyleProperty( TextStyleKeys.STRIKETHROUGH, Boolean.TRUE ); } if ( "underline".equals( value ) ) { result.getStyle().setStyleProperty( TextStyleKeys.UNDERLINED, Boolean.TRUE ); } } } final Object valign = attr.getAttribute( CSS.Attribute.VERTICAL_ALIGN ); if ( valign != null ) { final VerticalTextAlign valignValue = VerticalTextAlign.valueOf( String.valueOf( valign ) ); result.getStyle().setStyleProperty( TextStyleKeys.VERTICAL_TEXT_ALIGNMENT, valignValue ); try { result.getStyle().setStyleProperty( ElementStyleKeys.VALIGNMENT, ReportParserUtil.parseVerticalElementAlignment( String.valueOf( valign ), null ) ); } catch ( ParseException e ) { // ignore .. } } final Object whitespaceText = attr.getAttribute( CSS.Attribute.WHITE_SPACE ); if ( whitespaceText != null ) { final String value = String.valueOf( whitespaceText ); if ( "pre".equals( value ) ) { result.getStyle().setStyleProperty( TextStyleKeys.WHITE_SPACE_COLLAPSE, WhitespaceCollapse.PRESERVE ); result.getStyle().setStyleProperty( TextStyleKeys.TEXT_WRAP, TextWrap.NONE ); } else if ( "nowrap".equals( value ) ) { result.getStyle().setStyleProperty( TextStyleKeys.WHITE_SPACE_COLLAPSE, WhitespaceCollapse.PRESERVE_BREAKS ); result.getStyle().setStyleProperty( TextStyleKeys.TEXT_WRAP, TextWrap.NONE ); } else { result.getStyle().setStyleProperty( TextStyleKeys.WHITE_SPACE_COLLAPSE, WhitespaceCollapse.COLLAPSE ); result.getStyle().setStyleProperty( TextStyleKeys.TEXT_WRAP, TextWrap.WRAP ); } } else { result.getStyle().setStyleProperty( TextStyleKeys.WHITE_SPACE_COLLAPSE, WhitespaceCollapse.COLLAPSE ); result.getStyle().setStyleProperty( TextStyleKeys.TEXT_WRAP, TextWrap.WRAP ); } final Object alignAttribute = attr.getAttribute( HTML.Attribute.ALIGN ); if ( alignAttribute != null ) { try { result.getStyle().setStyleProperty( ElementStyleKeys.ALIGNMENT, ReportParserUtil.parseHorizontalElementAlignment( String.valueOf( alignAttribute ), null ) ); } catch ( ParseException e ) { // ignore .. } } final Object titleAttribute = attr.getAttribute( HTML.Attribute.TITLE ); if ( titleAttribute != null ) { result.setAttribute( AttributeNames.Html.NAMESPACE, AttributeNames.Html.TITLE, String.valueOf( titleAttribute ) ); } final Object hrefAttribute = attr.getAttribute( HTML.Attribute.HREF ); if ( hrefAttribute != null ) { result.getStyle().setStyleProperty( ElementStyleKeys.HREF_TARGET, String.valueOf( hrefAttribute ) ); } final Object textIndentStyle = attr.getAttribute( CSS.Attribute.TEXT_INDENT ); if ( textIndentStyle != null ) { result.getStyle().setStyleProperty( TextStyleKeys.FIRST_LINE_INDENT, parseLength( String.valueOf( textIndentStyle ) ) ); } // attr.getAttribute(CSS.Attribute.LIST_STYLE_TYPE); // attr.getAttribute(CSS.Attribute.LIST_STYLE_TYPE); // attr.getAttribute(CSS.Attribute.LIST_STYLE_POSITION); } private HTML.Tag findTag( final AttributeSet attr ) { final Enumeration names = attr.getAttributeNames(); while ( names.hasMoreElements() ) { final Object name = names.nextElement(); final Object o = attr.getAttribute( name ); if ( o instanceof HTML.Tag ) { if ( HTML.Tag.CONTENT == o ) { continue; } if ( HTML.Tag.COMMENT == o ) { continue; } return (HTML.Tag) o; } } return null; } private void parseBoxStyle( final Element result, final AttributeSet attr ) { final Object paddingText = attr.getAttribute( CSS.Attribute.PADDING ); if ( paddingText != null ) { final Float padding = parseLength( String.valueOf( paddingText ) ); result.getStyle().setStyleProperty( ElementStyleKeys.PADDING_TOP, padding ); result.getStyle().setStyleProperty( ElementStyleKeys.PADDING_LEFT, padding ); result.getStyle().setStyleProperty( ElementStyleKeys.PADDING_BOTTOM, padding ); result.getStyle().setStyleProperty( ElementStyleKeys.PADDING_RIGHT, padding ); } final Object paddingTop = attr.getAttribute( CSS.Attribute.PADDING_TOP ); if ( paddingTop != null ) { final Float padding = parseLength( String.valueOf( paddingTop ) ); result.getStyle().setStyleProperty( ElementStyleKeys.PADDING_TOP, padding ); } final Object paddingLeft = attr.getAttribute( CSS.Attribute.PADDING_LEFT ); if ( paddingLeft != null ) { final Float padding = parseLength( String.valueOf( paddingLeft ) ); result.getStyle().setStyleProperty( ElementStyleKeys.PADDING_LEFT, padding ); } final Object paddingBottom = attr.getAttribute( CSS.Attribute.PADDING_BOTTOM ); if ( paddingBottom != null ) { final Float padding = parseLength( String.valueOf( paddingBottom ) ); result.getStyle().setStyleProperty( ElementStyleKeys.PADDING_BOTTOM, padding ); } final Object paddingRight = attr.getAttribute( CSS.Attribute.PADDING_RIGHT ); if ( paddingRight != null ) { final Float padding = parseLength( String.valueOf( paddingRight ) ); result.getStyle().setStyleProperty( ElementStyleKeys.PADDING_RIGHT, padding ); } final Object heightText = attr.getAttribute( CSS.Attribute.HEIGHT ); if ( heightText != null ) { result.getStyle().setStyleProperty( ElementStyleKeys.MIN_HEIGHT, parseLength( String.valueOf( heightText ) ) ); } final Object widthText = attr.getAttribute( CSS.Attribute.WIDTH ); if ( widthText != null ) { result.getStyle().setStyleProperty( ElementStyleKeys.MIN_WIDTH, parseLength( String.valueOf( widthText ) ) ); } } private void parseBorderAndBackgroundStyle( final Element result, final StyleSheet sheet, final AttributeSet attr ) { final Object backgroundColor = attr.getAttribute( CSS.Attribute.BACKGROUND_COLOR ); if ( backgroundColor != null ) { result.getStyle().setStyleProperty( ElementStyleKeys.BACKGROUND_COLOR, sheet.stringToColor( String.valueOf( backgroundColor ) ) ); } final Object borderStyleText = attr.getAttribute( CSS.Attribute.BORDER_STYLE ); if ( borderStyleText != null ) { final BorderStyle borderStyle = BorderStyle.getBorderStyle( String.valueOf( borderStyleText ) ); if ( borderStyle != null ) { result.getStyle().setStyleProperty( ElementStyleKeys.BORDER_BOTTOM_STYLE, borderStyle ); result.getStyle().setStyleProperty( ElementStyleKeys.BORDER_TOP_STYLE, borderStyle ); result.getStyle().setStyleProperty( ElementStyleKeys.BORDER_LEFT_STYLE, borderStyle ); result.getStyle().setStyleProperty( ElementStyleKeys.BORDER_RIGHT_STYLE, borderStyle ); } } final Object borderWidthText = attr.getAttribute( CSS.Attribute.BORDER_WIDTH ); if ( borderWidthText != null ) { final Float borderWidth = parseLength( String.valueOf( borderWidthText ) ); result.getStyle().setStyleProperty( ElementStyleKeys.BORDER_BOTTOM_WIDTH, borderWidth ); result.getStyle().setStyleProperty( ElementStyleKeys.BORDER_TOP_WIDTH, borderWidth ); result.getStyle().setStyleProperty( ElementStyleKeys.BORDER_LEFT_WIDTH, borderWidth ); result.getStyle().setStyleProperty( ElementStyleKeys.BORDER_RIGHT_WIDTH, borderWidth ); } final Object borderBottomWidthText = attr.getAttribute( CSS.Attribute.BORDER_BOTTOM_WIDTH ); if ( borderBottomWidthText != null ) { final Float borderWidth = parseLength( String.valueOf( borderBottomWidthText ) ); result.getStyle().setStyleProperty( ElementStyleKeys.BORDER_BOTTOM_WIDTH, borderWidth ); } final Object borderRightWidthText = attr.getAttribute( CSS.Attribute.BORDER_RIGHT_WIDTH ); if ( borderRightWidthText != null ) { final Float borderWidth = parseLength( String.valueOf( borderRightWidthText ) ); result.getStyle().setStyleProperty( ElementStyleKeys.BORDER_RIGHT_WIDTH, borderWidth ); } final Object borderTopWidthText = attr.getAttribute( CSS.Attribute.BORDER_TOP_WIDTH ); if ( borderTopWidthText != null ) { final Float borderWidth = parseLength( String.valueOf( borderTopWidthText ) ); result.getStyle().setStyleProperty( ElementStyleKeys.BORDER_TOP_WIDTH, borderWidth ); } final Object borderLeftWidth = attr.getAttribute( CSS.Attribute.BORDER_LEFT_WIDTH ); if ( borderLeftWidth != null ) { final Float borderWidth = parseLength( String.valueOf( borderLeftWidth ) ); result.getStyle().setStyleProperty( ElementStyleKeys.BORDER_LEFT_WIDTH, borderWidth ); } final Object colorText = attr.getAttribute( CSS.Attribute.COLOR ); if ( colorText != null ) { final Color color = sheet.stringToColor( String.valueOf( colorText ) ); result.getStyle().setStyleProperty( ElementStyleKeys.BORDER_BOTTOM_COLOR, color ); result.getStyle().setStyleProperty( ElementStyleKeys.BORDER_TOP_COLOR, color ); result.getStyle().setStyleProperty( ElementStyleKeys.BORDER_LEFT_COLOR, color ); result.getStyle().setStyleProperty( ElementStyleKeys.BORDER_RIGHT_COLOR, color ); result.getStyle().setStyleProperty( ElementStyleKeys.PAINT, color ); } final Object borderColorText = attr.getAttribute( CSS.Attribute.BORDER_COLOR ); if ( borderColorText != null ) { final Color borderColor = sheet.stringToColor( String.valueOf( borderColorText ) ); result.getStyle().setStyleProperty( ElementStyleKeys.BORDER_BOTTOM_COLOR, borderColor ); result.getStyle().setStyleProperty( ElementStyleKeys.BORDER_TOP_COLOR, borderColor ); result.getStyle().setStyleProperty( ElementStyleKeys.BORDER_LEFT_COLOR, borderColor ); result.getStyle().setStyleProperty( ElementStyleKeys.BORDER_RIGHT_COLOR, borderColor ); } } private Float parseLength( final String value ) { if ( value == null ) { return null; } try { final StreamTokenizer strtok = new StreamTokenizer( new StringReader( value ) ); strtok.parseNumbers(); final int firstToken = strtok.nextToken(); if ( firstToken != StreamTokenizer.TT_NUMBER ) { return null; } final double nval = strtok.nval; final int nextToken = strtok.nextToken(); if ( nextToken != StreamTokenizer.TT_WORD ) { // yeah, this is against the standard, but we are dealing with deadly ugly non-standard documents here // maybe we will be able to integrate a real HTML processor at some point. return new Float( nval ); } final String unit = strtok.sval; if ( "%".equals( unit ) ) { return new Float( -nval ); } if ( "cm".equals( unit ) ) { return new Float( nval * 25.4 / 72 ); } if ( "mm".equals( unit ) ) { return new Float( nval * 2.54 / 72 ); } if ( "pt".equals( unit ) ) { return new Float( nval ); } if ( "in".equals( unit ) ) { return new Float( nval * 72 ); } if ( "px".equals( unit ) ) { return new Float( nval * 72 ); } if ( "pc".equals( unit ) ) { return new Float( nval * 12 ); } return null; } catch ( IOException ioe ) { return null; } } private void configureBand( final javax.swing.text.Element textElement, final Band band ) { final HTML.Tag tag = findTag( textElement.getAttributes() ); if ( tag == null ) { if ( "paragraph".equals( textElement.getName() ) || "section".equals( textElement.getName() ) ) { band.getStyle().setStyleProperty( BandStyleKeys.LAYOUT, "block" ); band.getStyle().setStyleProperty( ElementStyleKeys.MIN_WIDTH, new Float( -100 ) ); band.setName( textElement.getName() ); } else { band.getStyle().setStyleProperty( BandStyleKeys.LAYOUT, "inline" ); band.setName( textElement.getName() ); } return; } if ( BLOCK_ELEMENTS.contains( tag ) ) { band.getStyle().setStyleProperty( BandStyleKeys.LAYOUT, "block" ); band.getStyle().setStyleProperty( ElementStyleKeys.MIN_WIDTH, new Float( -100 ) ); band.setName( String.valueOf( tag ) ); } else { band.getStyle().setStyleProperty( BandStyleKeys.LAYOUT, "inline" ); band.setName( String.valueOf( tag ) ); } } }