/* * 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 - 2016 Pentaho Corporation.. All rights reserved. */ package org.pentaho.reporting.engine.classic.core.modules.output.table.html.helper; import org.pentaho.reporting.engine.classic.core.AttributeNames; import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot; import org.pentaho.reporting.engine.classic.core.ElementAlignment; import org.pentaho.reporting.engine.classic.core.ImageContainer; import org.pentaho.reporting.engine.classic.core.ReportAttributeMap; import org.pentaho.reporting.engine.classic.core.URLImageContainer; import org.pentaho.reporting.engine.classic.core.imagemap.ImageMap; import org.pentaho.reporting.engine.classic.core.imagemap.parser.ImageMapWriter; import org.pentaho.reporting.engine.classic.core.layout.model.context.BoxDefinition; import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorMetaData; import org.pentaho.reporting.engine.classic.core.layout.output.RenderUtility; import org.pentaho.reporting.engine.classic.core.modules.output.table.base.AbstractTableOutputProcessor; import org.pentaho.reporting.engine.classic.core.modules.output.table.html.HtmlContentGenerator; import org.pentaho.reporting.engine.classic.core.modules.output.table.html.HtmlPrinter; 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.TextRotation; import org.pentaho.reporting.engine.classic.core.util.InstanceID; import org.pentaho.reporting.engine.classic.core.util.RotatedTextDrawable; import org.pentaho.reporting.engine.classic.core.util.geom.StrictBounds; import org.pentaho.reporting.engine.classic.core.util.geom.StrictGeomUtility; import org.pentaho.reporting.libraries.base.util.ArgumentNullException; import org.pentaho.reporting.libraries.base.util.StringUtils; import org.pentaho.reporting.libraries.repository.ContentIOException; import org.pentaho.reporting.libraries.resourceloader.ResourceKey; import org.pentaho.reporting.libraries.resourceloader.factory.drawable.DrawableWrapper; import org.pentaho.reporting.libraries.xmlns.common.AttributeList; import org.pentaho.reporting.libraries.xmlns.writer.XmlWriter; import org.pentaho.reporting.libraries.xmlns.writer.XmlWriterSupport; import java.io.IOException; import java.text.NumberFormat; public class HtmlTextExtractorHelper { private static final String DIV_TAG = "div"; private static final String HREF_ATTR = "href"; private static final String TARGET_ATTR = "target"; private static final String TITLE_ATTR = "title"; private static final String A_TAG = "a"; private static final String BR_TAG = "br"; private static final String SPAN_TAG = "span"; private static final String IMG_TAG = "img"; private static final String SRC_ATTR = "src"; private static final String USEMAP_ATTR = "usemap"; private static final String PT_UNIT = "pt"; private static final String PX_UNIT = "px"; private static final String ALT_ATTR = "alt"; private HtmlTextExtractorState processStack; private InstanceID firstElement; private XmlWriter xmlWriter; private HtmlTagHelper tagHelper; private OutputProcessorMetaData metaData; private boolean enableInheritedLinkStyle; private HtmlContentGenerator contentGenerator; public HtmlTextExtractorHelper( final HtmlTagHelper tagHelper, final XmlWriter xmlWriter, final OutputProcessorMetaData metaData, final HtmlContentGenerator contentGenerator ) { ArgumentNullException.validate( "tagHelper", tagHelper ); ArgumentNullException.validate( "metaData", metaData ); ArgumentNullException.validate( "contentGenerator", contentGenerator ); ArgumentNullException.validate( "xmlWriter", xmlWriter ); this.tagHelper = tagHelper; this.metaData = metaData; this.contentGenerator = contentGenerator; this.enableInheritedLinkStyle = ( "true".equals( ClassicEngineBoot.getInstance().getGlobalConfig().getConfigProperty( "org.pentaho.reporting.engine.classic.core.modules.output.table.html.LinksInheritStyle" ) ) ); this.xmlWriter = xmlWriter; } public boolean isEnableInheritedLinkStyle() { return enableInheritedLinkStyle; } public void setFirstElement( final InstanceID firstElement, final HtmlTextExtractorState processStack ) { this.firstElement = firstElement; this.processStack = processStack; } public boolean startBox( final InstanceID box, final ReportAttributeMap attrs, final StyleSheet styleSheet, final BoxDefinition boxDefinition, final boolean forceTag ) { return startBox( box, attrs, styleSheet, boxDefinition, forceTag, DIV_TAG ); } public boolean startInlineBox( final InstanceID box, final ReportAttributeMap attrs, final StyleSheet styleSheet, final BoxDefinition boxDefinition ) { return startBox( box, attrs, styleSheet, boxDefinition, true, SPAN_TAG ); } private boolean startBox( final InstanceID box, final ReportAttributeMap attrs, final StyleSheet styleSheet, final BoxDefinition boxDefinition, final boolean forceTag, final String tag ) { try { if ( firstElement != box ) { final AttributeList attrList = new AttributeList(); HtmlTagHelper.applyHtmlAttributes( attrs, attrList ); final StyleBuilder styleBuilder = tagHelper.getStyleBuilder(); final StyleBuilder style = tagHelper.getStyleBuilderFactory().produceTextStyle( styleBuilder, styleSheet, boxDefinition, true, processStack.getStyle() ); tagHelper.getStyleManager().updateStyle( style, attrList ); if ( forceTag || attrList.isEmpty() == false ) { xmlWriter.writeTag( HtmlPrinter.XHTML_NAMESPACE, tag, attrList, XmlWriterSupport.OPEN ); processStack = new HtmlTextExtractorState( processStack, true ); } else { processStack = new HtmlTextExtractorState( processStack, true ); } writeLocalAnchor( styleSheet ); final Object rawContent = attrs.getAttribute( AttributeNames.Html.NAMESPACE, AttributeNames.Html.EXTRA_RAW_CONTENT ); if ( rawContent != null ) { xmlWriter.writeText( String.valueOf( rawContent ) ); } } else { processStack = new HtmlTextExtractorState( processStack, false ); } final String target = (String) styleSheet.getStyleProperty( ElementStyleKeys.HREF_TARGET ); if ( target != null ) { handleLinkOnElement( styleSheet, target ); processStack = new HtmlTextExtractorState( processStack, true ); } else { processStack = new HtmlTextExtractorState( processStack, false ); } if ( Boolean.TRUE.equals( attrs .getAttribute( AttributeNames.Html.NAMESPACE, AttributeNames.Html.SUPPRESS_CONTENT ) ) ) { return false; } return true; } catch ( IOException e ) { throw new HtmlOutputProcessingException( "Failed to perform IO", e ); } } public void finishBox( final InstanceID box, final ReportAttributeMap<Object> attributes ) { try { if ( processStack.isWrittenTag() ) { xmlWriter.writeCloseTag(); } processStack = processStack.getParent(); if ( firstElement != box ) { final Object rawFooterContent = attributes.getAttribute( AttributeNames.Html.NAMESPACE, AttributeNames.Html.EXTRA_RAW_FOOTER_CONTENT ); if ( rawFooterContent != null ) { xmlWriter.writeText( String.valueOf( rawFooterContent ) ); } } if ( processStack.isWrittenTag() ) { xmlWriter.writeCloseTag(); } processStack = processStack.getParent(); } catch ( IOException e ) { throw new HtmlOutputProcessingException( "Failed to perform IO", e ); } } public void writeLocalAnchor( final StyleSheet styleSheet ) throws IOException { final String anchor = (String) styleSheet.getStyleProperty( ElementStyleKeys.ANCHOR_NAME ); if ( anchor != null ) { xmlWriter.writeTag( HtmlPrinter.XHTML_NAMESPACE, A_TAG, "name", anchor, XmlWriterSupport.CLOSE ); } } public void handleLinkOnElement( final StyleSheet styleSheet, final String target ) throws IOException { final String window = (String) styleSheet.getStyleProperty( ElementStyleKeys.HREF_WINDOW ); final AttributeList linkAttr = new AttributeList(); linkAttr.setAttribute( HtmlPrinter.XHTML_NAMESPACE, HREF_ATTR, target ); if ( window != null && StringUtils.startsWithIgnoreCase( target, "javascript:" ) == false ) { // NON-NLS linkAttr.setAttribute( HtmlPrinter.XHTML_NAMESPACE, TARGET_ATTR, normalizeWindow( window ) ); } final String title = (String) styleSheet.getStyleProperty( ElementStyleKeys.HREF_TITLE ); if ( title != null ) { linkAttr.setAttribute( HtmlPrinter.XHTML_NAMESPACE, TITLE_ATTR, title ); } if ( enableInheritedLinkStyle ) { StyleBuilder styleBuilder = createLinkStyle( tagHelper.getStyleBuilder() ); tagHelper.getStyleManager().updateStyle( styleBuilder, linkAttr ); } xmlWriter.writeTag( HtmlPrinter.XHTML_NAMESPACE, A_TAG, linkAttr, XmlWriterSupport.OPEN ); } private String normalizeWindow( final String window ) { if ( "_top".equalsIgnoreCase( window ) ) { // NON-NLS return "_top"; // NON-NLS } if ( "_self".equalsIgnoreCase( window ) ) { // NON-NLS return "_self"; // NON-NLS } if ( "_parent".equalsIgnoreCase( window ) ) { // NON-NLS return "_parent"; // NON-NLS } if ( "_blank".equalsIgnoreCase( window ) ) { // NON-NLS return "_blank"; // NON-NLS } return window; } private StyleBuilder createLinkStyle( StyleBuilder b ) { if ( b == null ) { b = new DefaultStyleBuilder( tagHelper.getStyleBuilderFactory() ); } b.append( StyleBuilder.CSSKeys.FONT_STYLE, "inherit" ); b.append( StyleBuilder.CSSKeys.FONT_FAMILY, "inherit" ); b.append( StyleBuilder.CSSKeys.FONT_WEIGHT, "inherit" ); b.append( StyleBuilder.CSSKeys.FONT_SIZE, "inherit" ); b.append( StyleBuilder.CSSKeys.TEXT_DECORATION, "inherit" ); b.append( StyleBuilder.CSSKeys.COLOR, "inherit" ); return b; } public StyleBuilder produceClipStyle( final long nodeWidth, long nodeHeight ) { StyleBuilder styleBuilder = tagHelper.getStyleBuilder(); styleBuilder.clear(); // cuts down on object creation StyleBuilderFactory styleBuilderFactory = tagHelper.getStyleBuilderFactory(); final NumberFormat pointConverter = styleBuilder.getPointConverter(); styleBuilder.append( DefaultStyleBuilder.CSSKeys.OVERFLOW, "hidden" ); // NON-NLS styleBuilder.append( DefaultStyleBuilder.CSSKeys.WIDTH, pointConverter.format( styleBuilderFactory .fixLengthForSafari( StrictGeomUtility.toExternalValue( nodeWidth ) ) ), PT_UNIT ); styleBuilder.append( DefaultStyleBuilder.CSSKeys.HEIGHT, pointConverter.format( styleBuilderFactory .fixLengthForSafari( StrictGeomUtility.toExternalValue( nodeHeight ) ) ), PT_UNIT ); return styleBuilder; } /** * Populates the style builder with the style information for the image based on the RenderableReplacedContent * * @return the style-builder with the image style or null, if the image must be clipped. */ public StyleBuilder produceImageStyle( final StyleSheet styleSheet, final long nodeWidth, long nodeHeight, final long contentWidth, final long contentHeight ) { StyleBuilder styleBuilder = tagHelper.getStyleBuilder(); styleBuilder.clear(); // cuts down on object creation StyleBuilderFactory styleBuilderFactory = tagHelper.getStyleBuilderFactory(); final NumberFormat pointConverter = styleBuilder.getPointConverter(); final double scale = RenderUtility.getNormalizationScale( metaData ); if ( styleSheet.getBooleanStyleProperty( ElementStyleKeys.SCALE ) ) { if ( styleSheet.getBooleanStyleProperty( ElementStyleKeys.KEEP_ASPECT_RATIO ) && ( contentWidth > 0 && contentHeight > 0 ) ) { final double scaleFactor = Math.min( nodeWidth / (double) contentWidth, nodeHeight / (double) contentHeight ); styleBuilder.append( DefaultStyleBuilder.CSSKeys.WIDTH, pointConverter.format( styleBuilderFactory.fixLengthForSafari( StrictGeomUtility .toExternalValue( (long) ( contentWidth * scaleFactor * scale ) ) ) ), PX_UNIT ); styleBuilder.append( DefaultStyleBuilder.CSSKeys.HEIGHT, pointConverter.format( styleBuilderFactory.fixLengthForSafari( StrictGeomUtility .toExternalValue( (long) ( contentHeight * scaleFactor * scale ) ) ) ), PX_UNIT ); } else { styleBuilder.append( DefaultStyleBuilder.CSSKeys.WIDTH, pointConverter.format( styleBuilderFactory .fixLengthForSafari( StrictGeomUtility.toExternalValue( (long) ( nodeWidth * scale ) ) ) ), PX_UNIT ); styleBuilder.append( DefaultStyleBuilder.CSSKeys.HEIGHT, pointConverter.format( styleBuilderFactory .fixLengthForSafari( StrictGeomUtility.toExternalValue( (long) ( nodeHeight * scale ) ) ) ), PX_UNIT ); } } else { // for plain drawable content, there is no intrinsic-width or height, so we have to use the computed // width and height instead. if ( contentWidth > nodeWidth || contentHeight > nodeHeight ) { // There is clipping involved. The img-element does *not* receive a width or height property. // the width and height is applied to an external DIV element instead. return null; } if ( contentWidth == 0 && contentHeight == 0 ) { // Drawable content has no intrinsic height or width, therefore we must not use the content size at all. styleBuilder.append( DefaultStyleBuilder.CSSKeys.WIDTH, pointConverter.format( styleBuilderFactory .fixLengthForSafari( StrictGeomUtility.toExternalValue( (long) ( nodeWidth * scale ) ) ) ), PX_UNIT ); styleBuilder.append( DefaultStyleBuilder.CSSKeys.HEIGHT, pointConverter.format( styleBuilderFactory .fixLengthForSafari( StrictGeomUtility.toExternalValue( (long) ( nodeHeight * scale ) ) ) ), PX_UNIT ); } else { final long width = Math.min( nodeWidth, contentWidth ); final long height = Math.min( nodeHeight, contentHeight ); styleBuilder.append( DefaultStyleBuilder.CSSKeys.WIDTH, pointConverter.format( styleBuilderFactory .fixLengthForSafari( StrictGeomUtility.toExternalValue( (long) ( width * scale ) ) ) ), PX_UNIT ); styleBuilder.append( DefaultStyleBuilder.CSSKeys.HEIGHT, pointConverter.format( styleBuilderFactory .fixLengthForSafari( StrictGeomUtility.toExternalValue( (long) ( height * scale ) ) ) ), PX_UNIT ); } } return styleBuilder; } public boolean processRenderableReplacedContent( final ReportAttributeMap attrs, final StyleSheet styleSheet, final long width, final long height, final long contentWidth, final long contentHeight, final Object rawObject ) throws ContentIOException, IOException { // Fallback: (At the moment, we only support drawables and images.) if ( rawObject instanceof ImageContainer ) { if ( rawObject instanceof URLImageContainer ) { if ( tryHandleUrlImage( styleSheet, width, height, contentWidth, contentHeight, (URLImageContainer) rawObject ) ) { return true; } } if ( tryHandleLocalImageContainer( styleSheet, attrs, width, height, contentWidth, contentHeight, (ImageContainer) rawObject ) ) { return true; } return false; } if ( !metaData.isFeatureSupported( AbstractTableOutputProcessor.ROTATED_TEXT_AS_IMAGES ) ) { final RotatedTextDrawable rotatedTextDrawable = RotatedTextDrawable.extract( rawObject ); if ( rotatedTextDrawable != null ) { return tryHandleRotatedText( rotatedTextDrawable, styleSheet, width, height, contentWidth, contentHeight ); } } if ( rawObject instanceof DrawableWrapper ) { return tryHandleDrawable( attrs, width, height, contentWidth, contentHeight, styleSheet, (DrawableWrapper) rawObject ); } return false; } private String formatLength( long length ) { final StyleBuilder styleBuilder = tagHelper.getStyleBuilder(); final NumberFormat pointConverter = styleBuilder.getPointConverter(); StyleBuilderFactory sbf = tagHelper.getStyleBuilderFactory(); return pointConverter.format( sbf.fixLengthForSafari( StrictGeomUtility.toExternalValue( length ) ) ); } private boolean tryHandleRotatedText( final RotatedTextDrawable rotatedTextDrawable, final StyleSheet styleSheet, long width, long height, long contentWidth, long contentHeight ) throws IOException { boolean isMiddleVAlign = false; final StyleBuilder styleBuilder = tagHelper.getStyleBuilder(); StyleBuilderFactory sbf = tagHelper.getStyleBuilderFactory(); final StyleBuilder style = sbf.produceTextStyle( styleBuilder, styleSheet, null, false, processStack.getStyle() ); style.append( StyleBuilder.CSSKeys.WHITE_SPACE, "nowrap" ); style.append( StyleBuilder.CSSKeys.OVERFLOW, "hidden" ); style.append( StyleBuilder.CSSKeys.TRANSFORM_ORIGIN, "0 0" ); style.append( StyleBuilder.CSSKeys.WIDTH, formatLength( height ), PT_UNIT ); // Rotation +90 if ( rotatedTextDrawable.getRotation() == TextRotation.D_270 ) { // TOP if ( rotatedTextDrawable.getvAlign().equals( ElementAlignment.TOP ) ) { if ( rotatedTextDrawable.gethAlign().equals( ElementAlignment.LEFT ) || rotatedTextDrawable.gethAlign() .equals( ElementAlignment.JUSTIFY ) ) { style.append( StyleBuilder.CSSKeys.TRANSFORM, String.format( "translate(%spt,0pt) rotate(90deg)", formatLength( contentWidth ) ) ); } else if ( rotatedTextDrawable.gethAlign().equals( ElementAlignment.CENTER ) ) { style.append( StyleBuilder.CSSKeys.TRANSFORM, String.format( "translate(%spt,0pt) rotate(90deg)", formatLength( width / 2 + contentWidth / 2 ) ) ); } else if ( rotatedTextDrawable.gethAlign().equals( ElementAlignment.RIGHT ) ) { style.append( StyleBuilder.CSSKeys.TEXT_ALIGN, "left" ); style.append( StyleBuilder.CSSKeys.TRANSFORM, String.format( "translate(%spt,0pt) rotate(90deg)", formatLength( width ) ) ); } // MIDDLE } else if ( rotatedTextDrawable.getvAlign().equals( ElementAlignment.MIDDLE ) ) { isMiddleVAlign = true; style.append( StyleBuilder.CSSKeys.WIDTH, formatLength( contentHeight ), PT_UNIT ); if ( rotatedTextDrawable.gethAlign().equals( ElementAlignment.LEFT ) || rotatedTextDrawable.gethAlign() .equals( ElementAlignment.JUSTIFY ) ) { style.append( StyleBuilder.CSSKeys.TRANSFORM, String.format( "translate(%spt,-%spt) rotate(90deg)", formatLength( contentWidth ), formatLength( ( contentHeight - height ) / 2 ) ) ); } else if ( rotatedTextDrawable.gethAlign().equals( ElementAlignment.CENTER ) ) { style.append( StyleBuilder.CSSKeys.TRANSFORM, String.format( "translate(%spt,-%spt) rotate(90deg)", formatLength( width / 2 + contentWidth / 2 ), formatLength( ( contentHeight - height ) / 2 ) ) ); style.append( StyleBuilder.CSSKeys.TEXT_ALIGN, "left" ); } else if ( rotatedTextDrawable.gethAlign().equals( ElementAlignment.RIGHT ) ) { style.append( StyleBuilder.CSSKeys.TRANSFORM, String.format( "translate(%spt,-%spt) rotate(90deg)", formatLength( width ), formatLength( ( contentHeight - height ) / 2 ) ) ); style.append( StyleBuilder.CSSKeys.TEXT_ALIGN, "left" ); } // BOTTOM } else if ( rotatedTextDrawable.getvAlign().equals( ElementAlignment.BOTTOM ) ) { style.append( StyleBuilder.CSSKeys.DIRECTION, "rtl" ); if ( rotatedTextDrawable.gethAlign().equals( ElementAlignment.LEFT ) || rotatedTextDrawable.gethAlign() .equals( ElementAlignment.JUSTIFY ) ) { style.append( StyleBuilder.CSSKeys.TRANSFORM, String.format( "translate(%spt,-%spt) rotate(90deg)", formatLength( contentWidth ), formatLength( height - contentWidth ) ) ); } else if ( rotatedTextDrawable.gethAlign().equals( ElementAlignment.CENTER ) ) { style.append( StyleBuilder.CSSKeys.TRANSFORM, String.format( "translate(%spt,-%spt) rotate(90deg)", formatLength( width / 2 + contentWidth / 2 ), formatLength( height - contentWidth ) ) ); } else if ( rotatedTextDrawable.gethAlign().equals( ElementAlignment.RIGHT ) ) { style.append( StyleBuilder.CSSKeys.TRANSFORM, String.format( "translate(%spt,-%spt) rotate(90deg)", formatLength( width ), formatLength( height - contentWidth ) ) ); } } // Rotation -90 } else if ( rotatedTextDrawable.getRotation() == TextRotation.D_90 ) { // TOP if ( rotatedTextDrawable.getvAlign().equals( ElementAlignment.TOP ) ) { style.append( StyleBuilder.CSSKeys.DIRECTION, "rtl" ); if ( rotatedTextDrawable.gethAlign().equals( ElementAlignment.LEFT ) || rotatedTextDrawable.gethAlign() .equals( ElementAlignment.JUSTIFY ) ) { style.append( StyleBuilder.CSSKeys.TRANSFORM, String.format( "translate(0pt,%spt) rotate(-90deg)", formatLength( height ) ) ); } else if ( rotatedTextDrawable.gethAlign().equals( ElementAlignment.CENTER ) ) { style.append( StyleBuilder.CSSKeys.TRANSFORM, String.format( "translate(%spt,%spt) rotate(-90deg)", formatLength( width / 2 - contentWidth / 2 ), formatLength( height ) ) ); } else if ( rotatedTextDrawable.gethAlign().equals( ElementAlignment.RIGHT ) ) { style.append( StyleBuilder.CSSKeys.TRANSFORM, String.format( "translate(%spt,%spt) rotate(-90deg)", formatLength( width - contentWidth ), formatLength( height ) ) ); } // MIDDLE } else if ( rotatedTextDrawable.getvAlign().equals( ElementAlignment.MIDDLE ) ) { isMiddleVAlign = true; style.append( StyleBuilder.CSSKeys.WIDTH, formatLength( contentHeight ), PT_UNIT ); if ( rotatedTextDrawable.gethAlign().equals( ElementAlignment.LEFT ) || rotatedTextDrawable.gethAlign() .equals( ElementAlignment.JUSTIFY ) ) { style.append( StyleBuilder.CSSKeys.TRANSFORM, String.format( "translate(0pt, %spt) rotate(-90deg)", formatLength( height + ( contentHeight - height ) / 2 ) ) ); } else if ( rotatedTextDrawable.gethAlign().equals( ElementAlignment.CENTER ) ) { style.append( StyleBuilder.CSSKeys.TRANSFORM, String.format( "translate(%spt, %spt) rotate(-90deg)", formatLength( width / 2 - contentWidth / 2 ), formatLength( height + ( contentHeight - height ) / 2 ) ) ); style.append( StyleBuilder.CSSKeys.TEXT_ALIGN, "left" ); } else if ( rotatedTextDrawable.gethAlign().equals( ElementAlignment.RIGHT ) ) { style.append( StyleBuilder.CSSKeys.TRANSFORM, String.format( "translate(%spt, %spt) rotate(-90deg)", formatLength( width - contentWidth ), formatLength( height + ( contentHeight - height ) / 2 ) ) ); style.append( StyleBuilder.CSSKeys.TEXT_ALIGN, "left" ); } // BOTTOM } else if ( rotatedTextDrawable.getvAlign().equals( ElementAlignment.BOTTOM ) ) { if ( rotatedTextDrawable.gethAlign().equals( ElementAlignment.LEFT ) || rotatedTextDrawable.gethAlign() .equals( ElementAlignment.JUSTIFY ) ) { style.append( StyleBuilder.CSSKeys.TRANSFORM, String.format( "translate(0pt,%spt) rotate(-90deg)", formatLength( contentWidth ) ) ); } else if ( rotatedTextDrawable.gethAlign().equals( ElementAlignment.CENTER ) ) { style.append( StyleBuilder.CSSKeys.TRANSFORM, String.format( "translate(%spt,%spt) rotate(-90deg)", formatLength( width / 2 - contentWidth / 2 ), formatLength( contentWidth ) ) ); } else if ( rotatedTextDrawable.gethAlign().equals( ElementAlignment.RIGHT ) ) { style.append( StyleBuilder.CSSKeys.TRANSFORM, String.format( "translate(%spt,%spt) rotate(-90deg)", formatLength( width - contentWidth ), formatLength( contentWidth ) ) ); } } } AttributeList attributeList = new AttributeList(); tagHelper.getStyleManager().updateStyle( style, attributeList ); if ( isMiddleVAlign ) { AttributeList extDivAttributeList = new AttributeList(); final StyleBuilder extDivStyle = sbf.produceTextStyle( styleBuilder, styleSheet, null, false, processStack.getStyle() ); extDivStyle.append( StyleBuilder.CSSKeys.OVERFLOW, "hidden" ); extDivStyle.append( StyleBuilder.CSSKeys.HEIGHT, formatLength( height ), PT_UNIT ); tagHelper.getStyleManager().updateStyle( extDivStyle, extDivAttributeList ); xmlWriter.writeTag( HtmlPrinter.XHTML_NAMESPACE, DIV_TAG, extDivAttributeList, XmlWriter.OPEN ); xmlWriter.writeTag( HtmlPrinter.XHTML_NAMESPACE, DIV_TAG, attributeList, XmlWriter.OPEN ); xmlWriter.writeText( rotatedTextDrawable.getText() ); xmlWriter.writeCloseTag(); xmlWriter.writeCloseTag(); } else { xmlWriter.writeTag( HtmlPrinter.XHTML_NAMESPACE, DIV_TAG, attributeList, XmlWriter.OPEN ); xmlWriter.writeText( rotatedTextDrawable.getText() ); xmlWriter.writeCloseTag(); } return true; } private boolean tryHandleDrawable( final ReportAttributeMap attrs, final long width, final long height, final long contentWidth, final long contentHeight, final StyleSheet styleSheet, final DrawableWrapper drawable ) throws ContentIOException, IOException { // render it into an Buffered image and make it a PNG file. final StrictBounds cb = new StrictBounds( 0, 0, width, height ); final ImageContainer image = RenderUtility.createImageFromDrawable( drawable, cb, styleSheet, metaData ); if ( image == null ) { // xmlWriter.writeComment("Drawable content [No image generated]:" + source); return false; } final String type = RenderUtility.getEncoderType( attrs ); final float quality = RenderUtility.getEncoderQuality( attrs ); final String name = contentGenerator.writeImage( image, type, quality, true ); if ( name == null ) { // xmlWriter.writeComment("Drawable content [No image written]:" + source); return false; } // xmlWriter.writeComment("Drawable content:" + source); // Write image reference .. final AttributeList attrList = new AttributeList(); attrList.setAttribute( HtmlPrinter.XHTML_NAMESPACE, SRC_ATTR, name ); attrList.setAttribute( HtmlPrinter.XHTML_NAMESPACE, "border", "0" ); // NON-NLS final Object titleText = attrs.getAttribute( AttributeNames.Html.NAMESPACE, AttributeNames.Html.TITLE ); if ( titleText != null ) { attrList.setAttribute( HtmlPrinter.XHTML_NAMESPACE, TITLE_ATTR, String.valueOf( titleText ) ); } final Object altText = attrs.getAttribute( AttributeNames.Html.NAMESPACE, AttributeNames.Html.ALT ); if ( altText != null ) { attrList.setAttribute( HtmlPrinter.XHTML_NAMESPACE, ALT_ATTR, String.valueOf( altText ) ); } // width and height and scaling and so on .. // BI-SERVER 11651: The extractImageMap function will fill the "attrList" parameter with the name // of the image map, if there is one. So we have to call this method first. final ImageMap imageMap = extractImageMap( attrs, drawable, width, height, name, attrList ); writeImageTag( styleSheet, width, height, contentWidth, contentHeight, attrList ); if ( imageMap != null ) { ImageMapWriter.writeImageMap( xmlWriter, imageMap, RenderUtility.getNormalizationScale( metaData ) ); } return true; } private ImageMap extractImageMap( final ReportAttributeMap attributes, final Object rawObject, final long width, final long height, final String name, final AttributeList attrList ) { final ImageMap imageMap; final Object imageMapNameOverride = attributes.getAttribute( AttributeNames.Html.NAMESPACE, AttributeNames.Html.IMAGE_MAP_OVERRIDE ); if ( imageMapNameOverride != null ) { attrList.setAttribute( HtmlPrinter.XHTML_NAMESPACE, USEMAP_ATTR, String.valueOf( imageMapNameOverride ) ); imageMap = null; } else { // only generate a image map, if the user does not specify their own onw via the override. // Of course, they would have to provide the map by other means as well. imageMap = RenderUtility.extractImageMap( attributes, rawObject, width, height ); if ( imageMap != null ) { final String mapName = imageMap.getAttribute( HtmlPrinter.XHTML_NAMESPACE, "name" ); if ( mapName != null ) { attrList.setAttribute( HtmlPrinter.XHTML_NAMESPACE, USEMAP_ATTR, "#" + mapName ); } else { final String generatedName = "generated_" + name + "_map"; // NON-NLS imageMap.setAttribute( HtmlPrinter.XHTML_NAMESPACE, "name", generatedName ); // noinspection MagicCharacter attrList.setAttribute( HtmlPrinter.XHTML_NAMESPACE, USEMAP_ATTR, '#' + generatedName ); // NON-NLS } } } return imageMap; } private boolean tryHandleLocalImageContainer( final StyleSheet styleSheet, final ReportAttributeMap attributes, final long width, final long height, final long contentWidth, final long contentHeight, final ImageContainer rawObject ) throws ContentIOException, IOException { final String type = RenderUtility.getEncoderType( attributes ); final float quality = RenderUtility.getEncoderQuality( attributes ); // Make it a PNG file .. // xmlWriter.writeComment("Image content source:" + source); final String name = contentGenerator.writeImage( rawObject, type, quality, true ); if ( name == null ) { return false; } // Write image reference .. final AttributeList attrList = new AttributeList(); attrList.setAttribute( HtmlPrinter.XHTML_NAMESPACE, SRC_ATTR, name ); attrList.setAttribute( HtmlPrinter.XHTML_NAMESPACE, "border", "0" ); // NON-NLS final Object titleText = attributes.getAttribute( AttributeNames.Html.NAMESPACE, AttributeNames.Html.TITLE ); if ( titleText != null ) { attrList.setAttribute( HtmlPrinter.XHTML_NAMESPACE, TITLE_ATTR, String.valueOf( titleText ) ); } final Object altText = attributes.getAttribute( AttributeNames.Html.NAMESPACE, AttributeNames.Html.ALT ); if ( altText != null ) { attrList.setAttribute( HtmlPrinter.XHTML_NAMESPACE, ALT_ATTR, String.valueOf( altText ) ); } // width and height and scaling and so on .. writeImageTag( styleSheet, width, height, contentWidth, contentHeight, attrList ); final ImageMap imageMap = extractImageMap( attributes, null, width, height, name, attrList ); if ( imageMap != null ) { ImageMapWriter.writeImageMap( xmlWriter, imageMap, RenderUtility.getNormalizationScale( metaData ) ); } return true; } private boolean tryHandleUrlImage( final StyleSheet styleSheet, final long width, final long height, final long contentWidth, final long contentHeight, final URLImageContainer urlImageContainer ) throws ContentIOException, IOException { final ResourceKey source = urlImageContainer.getResourceKey(); if ( source != null ) { // Cool, we have access to the raw-data. Thats always nice as we // dont have to recode the whole thing. We can only recode images, not drawables. if ( contentGenerator.isRegistered( source ) == false ) { // Write image reference; return the name of the reference. This method will // return null, if the image is not recognized (it is no JPG, PNG or GIF image) final String name = contentGenerator.writeRaw( source ); if ( name != null ) { // Write image reference .. final AttributeList attrList = new AttributeList(); attrList.setAttribute( HtmlPrinter.XHTML_NAMESPACE, SRC_ATTR, name ); attrList.setAttribute( HtmlPrinter.XHTML_NAMESPACE, "border", "0" ); // NON-NLS // width and height and scaling and so on .. writeImageTag( styleSheet, width, height, contentWidth, contentHeight, attrList ); contentGenerator.registerContent( source, name ); return true; } else { // Mark this object as non-readable. This way we dont retry the failed operation // over and over again. contentGenerator.registerFailure( source ); } } else { final String cachedName = contentGenerator.getRegisteredName( source ); if ( cachedName != null ) { final AttributeList attrList = new AttributeList(); attrList.setAttribute( HtmlPrinter.XHTML_NAMESPACE, SRC_ATTR, cachedName ); attrList.setAttribute( HtmlPrinter.XHTML_NAMESPACE, "border", "0" ); // NON-NLS writeImageTag( styleSheet, width, height, contentWidth, contentHeight, attrList ); return true; } } } return false; } private void writeImageTag( final StyleSheet styleSheet, final long width, final long height, final long contentWidth, final long contentHeight, AttributeList attrList ) throws IOException { final StyleBuilder imgStyle = produceImageStyle( styleSheet, width, height, contentWidth, contentHeight ); if ( imgStyle == null ) { final AttributeList clipAttrList = new AttributeList(); final StyleBuilder divStyle = produceClipStyle( width, height ); tagHelper.getStyleManager().updateStyle( divStyle, clipAttrList ); xmlWriter.writeTag( HtmlPrinter.XHTML_NAMESPACE, DIV_TAG, clipAttrList, XmlWriterSupport.OPEN ); xmlWriter.writeTag( HtmlPrinter.XHTML_NAMESPACE, IMG_TAG, attrList, XmlWriterSupport.CLOSE ); xmlWriter.writeCloseTag(); } else { tagHelper.getStyleManager().updateStyle( imgStyle, attrList ); xmlWriter.writeTag( HtmlPrinter.XHTML_NAMESPACE, IMG_TAG, attrList, XmlWriterSupport.CLOSE ); } } }