/* * 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.xml.internal; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.reporting.engine.classic.core.AttributeNames; import org.pentaho.reporting.engine.classic.core.InvalidReportStateException; import org.pentaho.reporting.engine.classic.core.ReportAttributeMap; import org.pentaho.reporting.engine.classic.core.layout.model.BlockRenderBox; 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.CanvasRenderBox; import org.pentaho.reporting.engine.classic.core.layout.model.InlineRenderBox; import org.pentaho.reporting.engine.classic.core.layout.model.LayoutNodeTypes; import org.pentaho.reporting.engine.classic.core.layout.model.LogicalPageBox; import org.pentaho.reporting.engine.classic.core.layout.model.PageGrid; import org.pentaho.reporting.engine.classic.core.layout.model.ParagraphRenderBox; import org.pentaho.reporting.engine.classic.core.layout.model.PhysicalPageBox; import org.pentaho.reporting.engine.classic.core.layout.model.RenderBox; import org.pentaho.reporting.engine.classic.core.layout.model.RenderLength; import org.pentaho.reporting.engine.classic.core.layout.model.RenderNode; import org.pentaho.reporting.engine.classic.core.layout.model.RenderableComplexText; import org.pentaho.reporting.engine.classic.core.layout.model.RenderableReplacedContent; import org.pentaho.reporting.engine.classic.core.layout.model.RenderableReplacedContentBox; import org.pentaho.reporting.engine.classic.core.layout.model.RenderableText; import org.pentaho.reporting.engine.classic.core.layout.model.SpacerRenderNode; 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.layout.model.table.TableCellRenderBox; import org.pentaho.reporting.engine.classic.core.layout.model.table.TableColumnGroupNode; import org.pentaho.reporting.engine.classic.core.layout.model.table.TableRenderBox; import org.pentaho.reporting.engine.classic.core.layout.model.table.TableRowRenderBox; import org.pentaho.reporting.engine.classic.core.layout.model.table.TableSectionRenderBox; import org.pentaho.reporting.engine.classic.core.layout.output.LogicalPageKey; import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorMetaData; import org.pentaho.reporting.engine.classic.core.layout.process.IterateStructuralProcessStep; import org.pentaho.reporting.engine.classic.core.metadata.AttributeMetaData; import org.pentaho.reporting.engine.classic.core.metadata.ElementType; import org.pentaho.reporting.engine.classic.core.style.ElementStyleKeys; import org.pentaho.reporting.engine.classic.core.style.TextStyleKeys; import org.pentaho.reporting.engine.classic.core.util.beans.BeanException; import org.pentaho.reporting.engine.classic.core.util.beans.ColorValueConverter; import org.pentaho.reporting.engine.classic.core.util.beans.ConverterRegistry; 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.StringUtils; import org.pentaho.reporting.libraries.fonts.encoding.EncodingRegistry; import org.pentaho.reporting.libraries.formatting.FastDecimalFormat; import org.pentaho.reporting.libraries.resourceloader.ResourceKey; import org.pentaho.reporting.libraries.xmlns.common.AttributeList; import org.pentaho.reporting.libraries.xmlns.common.AttributeMap; import org.pentaho.reporting.libraries.xmlns.writer.DefaultTagDescription; import org.pentaho.reporting.libraries.xmlns.writer.XmlWriter; import java.awt.Color; import java.beans.PropertyEditor; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.Arrays; import java.util.Comparator; import java.util.Locale; import java.util.Set; /** * @noinspection HardCodedStringLiteral */ public class XmlDocumentWriter extends IterateStructuralProcessStep { private static final String LAYOUT_OUTPUT_NAMESPACE = "http://reporting.pentaho.org/namespaces/output/layout-output/pageable/1.0"; private static final Log logger = LogFactory.getLog( XmlDocumentWriter.class ); private OutputStream outputStream; private StrictBounds drawArea; private XmlWriter xmlWriter; private FastDecimalFormat pointIntConverter; private FastDecimalFormat pointConverter; private FastDecimalFormat pointShortConverter; private boolean ignoreEmptyBorders = true; private OutputProcessorMetaData metaData; public XmlDocumentWriter( final OutputStream outputStream, final OutputProcessorMetaData metaData ) { this.metaData = metaData; if ( outputStream == null ) { throw new NullPointerException(); } this.outputStream = outputStream; this.pointConverter = new FastDecimalFormat( "0.0####", Locale.US ); this.pointShortConverter = new FastDecimalFormat( "0.#####", Locale.US ); this.pointIntConverter = new FastDecimalFormat( "0", Locale.US ); } public void open() throws IOException { final DefaultTagDescription td = new DefaultTagDescription(); td.setNamespaceHasCData( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, false ); td.setElementHasCData( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "text", true ); // prepare anything that might needed to be prepared .. final String encoding = metaData.getConfiguration().getConfigProperty( "org.pentaho.reporting.engine.classic.core.modules.output.pageable.xml.Encoding", EncodingRegistry.getPlatformDefaultEncoding() ); final Writer writer = new BufferedWriter( new OutputStreamWriter( outputStream, encoding ) ); this.xmlWriter = new XmlWriter( writer, td ); this.xmlWriter.writeXmlDeclaration( encoding ); final AttributeList attrs = new AttributeList(); attrs.addNamespaceDeclaration( "", XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE ); xmlWriter.writeTag( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "layout-output", attrs, XmlWriter.OPEN ); } public void close() throws IOException { // close all .. xmlWriter.writeCloseTag(); xmlWriter.close(); } public void processPhysicalPage( final PageGrid pageGrid, final LogicalPageBox logicalPage, final int row, final int col ) throws IOException { final PhysicalPageBox page = pageGrid.getPage( row, col ); if ( page == null ) { return; } final float width = (float) StrictGeomUtility.toExternalValue( page.getWidth() ); final float height = (float) StrictGeomUtility.toExternalValue( page.getHeight() ); final float marginLeft = (float) StrictGeomUtility.toExternalValue( page.getImageableX() ); final float marginRight = (float) StrictGeomUtility.toExternalValue( page.getWidth() - page.getImageableWidth() - page.getImageableX() ); final float marginTop = (float) StrictGeomUtility.toExternalValue( page.getImageableY() ); final float marginBottom = (float) StrictGeomUtility.toExternalValue( page.getHeight() - page.getImageableHeight() - page.getImageableY() ); final AttributeList pageAttributes = new AttributeList(); pageAttributes.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "page-x", pointShortConverter.format( page.getGlobalX() ) ); pageAttributes.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "page-y", pointShortConverter.format( page.getGlobalY() ) ); pageAttributes.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "page-width", pointShortConverter.format( width ) ); pageAttributes.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "page-height", pointShortConverter.format( height ) ); pageAttributes.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "margin-top", pointShortConverter.format( marginTop ) ); pageAttributes.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "margin-left", pointShortConverter.format( marginLeft ) ); pageAttributes.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "margin-bottom", pointShortConverter.format( marginBottom ) ); pageAttributes.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "margin-right", pointShortConverter.format( marginRight ) ); xmlWriter.writeTag( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "physical-page", pageAttributes, XmlWriter.OPEN ); // and now process the box .. drawArea = new StrictBounds( page.getGlobalX(), page.getGlobalY(), page.getImageableWidth(), page.getImageableHeight() ); processPage( logicalPage ); xmlWriter.writeCloseTag(); } public void processLogicalPage( final LogicalPageKey key, final LogicalPageBox logicalPage ) throws IOException { final float width = (float) StrictGeomUtility.toExternalValue( logicalPage.getPageWidth() ); final float height = (float) StrictGeomUtility.toExternalValue( logicalPage.getPageHeight() ); final AttributeList pageAttributes = new AttributeList(); pageAttributes.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "logical-page-number", pointIntConverter .format( key.getPosition() ) ); pageAttributes.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "width", pointConverter.format( width ) ); pageAttributes.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "height", pointConverter.format( height ) ); xmlWriter.writeTag( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "logical-page", pageAttributes, XmlWriter.OPEN ); // and now process the box .. drawArea = new StrictBounds( 0, 0, logicalPage.getPageWidth(), logicalPage.getPageHeight() ); processPage( logicalPage ); xmlWriter.writeCloseTag(); } protected void processPage( final LogicalPageBox rootBox ) { final StrictBounds pageBounds = drawArea; startProcessing( rootBox.getWatermarkArea() ); final BlockRenderBox headerArea = rootBox.getHeaderArea(); final BlockRenderBox footerArea = rootBox.getFooterArea(); final BlockRenderBox repeatFooterArea = rootBox.getRepeatFooterArea(); final StrictBounds headerBounds = new StrictBounds( headerArea.getX(), headerArea.getY(), headerArea.getWidth(), headerArea.getHeight() ); final StrictBounds footerBounds = new StrictBounds( footerArea.getX(), footerArea.getY(), footerArea.getWidth(), footerArea.getHeight() ); final StrictBounds repeatFooterBounds = new StrictBounds( repeatFooterArea.getX(), repeatFooterArea.getY(), repeatFooterArea.getWidth(), repeatFooterArea.getHeight() ); final StrictBounds contentBounds = new StrictBounds( rootBox.getX(), headerArea.getY() + headerArea.getHeight(), rootBox.getWidth(), footerArea .getY() - headerArea.getHeight() ); this.drawArea = headerBounds; startProcessing( headerArea ); this.drawArea = contentBounds; processBoxChilds( rootBox ); this.drawArea = repeatFooterBounds; startProcessing( repeatFooterArea ); this.drawArea = footerBounds; startProcessing( footerArea ); this.drawArea = pageBounds; } private void writeElementAttributes( final RenderNode element ) throws IOException { final ReportAttributeMap attributes = element.getAttributes(); final ElementType type = element.getElementType(); final Set<AttributeMap.DualKey> collection = attributes.keySet(); final AttributeMap.DualKey[] attributeNames = collection.toArray( new AttributeMap.DualKey[ collection.size() ] ); Arrays.sort( attributeNames, new DualKeySorter() ); for ( int j = 0; j < attributeNames.length; j++ ) { final String namespace = attributeNames[j].namespace; if ( AttributeNames.Designtime.NAMESPACE.equals( namespace ) ) { continue; } final String name = attributeNames[j].name; final Object value = attributes.getAttribute( namespace, name ); if ( value == null ) { continue; } if ( metaData.isFeatureSupported( XmlPageOutputProcessorMetaData.WRITE_RESOURCEKEYS ) == false && value instanceof ResourceKey ) { continue; } final AttributeMetaData attrMeta = type.getMetaData().getAttributeDescription( namespace, name ); if ( attrMeta == null ) { // if you want to use attributes in this output target, declare the attribute's metadata first. continue; } final AttributeList attList = new AttributeList(); if ( value instanceof String ) { final String s = (String) value; if ( StringUtils.isEmpty( s ) ) { continue; } if ( xmlWriter.isNamespaceDefined( namespace ) == false && attList.isNamespaceUriDefined( namespace ) == false ) { attList.addNamespaceDeclaration( "autoGenNs", namespace ); } // preserve strings, but discard anything else. Until a attribute has a definition, we cannot // hope to understand the attribute's value. String-attributes can be expressed in XML easily, // and string is also how all unknown attributes are stored by the parser. attList.setAttribute( namespace, name, String.valueOf( value ) ); this.xmlWriter.writeTag( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "attribute", attList, XmlWriter.CLOSE ); continue; } if ( xmlWriter.isNamespaceDefined( namespace ) == false && attList.isNamespaceUriDefined( namespace ) == false ) { attList.addNamespaceDeclaration( "autoGenNs", namespace ); } try { final PropertyEditor propertyEditor = attrMeta.getEditor(); final String textValue; if ( propertyEditor != null ) { propertyEditor.setValue( value ); textValue = propertyEditor.getAsText(); } else { textValue = ConverterRegistry.toAttributeValue( value ); } if ( textValue != null ) { if ( "".equals( textValue ) == false ) { attList.setAttribute( namespace, name, textValue ); this.xmlWriter .writeTag( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "attribute", attList, XmlWriter.CLOSE ); } } else { if ( value instanceof ResourceKey ) { final ResourceKey reskey = (ResourceKey) value; final String identifierAsString = reskey.getIdentifierAsString(); attList.setAttribute( namespace, name, "resource-key:" + reskey.getSchema() + ":" + identifierAsString ); this.xmlWriter .writeTag( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "attribute", attList, XmlWriter.CLOSE ); } else { XmlDocumentWriter.logger.debug( "Attribute '" + namespace + '|' + name + "' is not convertible to a text - returned null: " + value ); } } } catch ( BeanException e ) { if ( attrMeta.isTransient() == false ) { XmlDocumentWriter.logger.warn( "Attribute '" + namespace + '|' + name + "' is not convertible with the bean-methods" ); } else { XmlDocumentWriter.logger.debug( "Attribute '" + namespace + '|' + name + "' is not convertible with the bean-methods" ); } } } } private AttributeList createBoxAttributeList( final RenderBox box ) { final AttributeList attributeList = new AttributeList(); if ( StringUtils.isEmpty( box.getName() ) == false ) { attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "name", box.getName() ); } attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "type", box.getClass().getSimpleName() ); final BoxDefinition definition = box.getBoxDefinition(); final Border border = definition.getBorder(); final StaticBoxLayoutProperties sblp = box.getStaticBoxLayoutProperties(); final BorderEdge top = border.getTop(); if ( BorderEdge.EMPTY.equals( top ) == false || ignoreEmptyBorders == false ) { attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "border-top-color", convertColorToString( top ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "border-top-width", pointConverter.format( StrictGeomUtility.toExternalValue( sblp.getBorderTop() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "border-top-style", String.valueOf( top.getBorderStyle() ) ); } final BorderEdge left = border.getLeft(); if ( BorderEdge.EMPTY.equals( left ) == false || ignoreEmptyBorders == false ) { attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "border-left-color", convertColorToString( left ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "border-left-width", pointConverter.format( StrictGeomUtility.toExternalValue( sblp.getBorderLeft() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "border-left-style", String.valueOf( left.getBorderStyle() ) ); } final BorderEdge bottom = border.getBottom(); if ( BorderEdge.EMPTY.equals( bottom ) == false || ignoreEmptyBorders == false ) { attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "border-bottom-color", convertColorToString( bottom ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "border-bottom-width", pointConverter.format( StrictGeomUtility.toExternalValue( sblp.getBorderBottom() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "border-bottom-style", String.valueOf( bottom.getBorderStyle() ) ); } final BorderEdge right = border.getRight(); if ( BorderEdge.EMPTY.equals( right ) == false || ignoreEmptyBorders == false ) { attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "border-right-color", convertColorToString( right ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "border-right-width", pointConverter.format( StrictGeomUtility.toExternalValue( sblp.getBorderRight() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "border-right-style", String.valueOf( right.getBorderStyle() ) ); } final BorderCorner topLeft = border.getTopLeft(); if ( isEmptyCorner( topLeft ) == false ) { attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "border-top-left-x", pointConverter.format( StrictGeomUtility.toExternalValue( topLeft.getWidth() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "border-top-left-y", pointConverter.format( StrictGeomUtility.toExternalValue( topLeft.getHeight() ) ) ); } final BorderCorner topRight = border.getTopRight(); if ( isEmptyCorner( topRight ) == false ) { attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "border-top-right-x", pointConverter.format( StrictGeomUtility.toExternalValue( topRight.getWidth() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "border-top-right-y", pointConverter.format( StrictGeomUtility.toExternalValue( topRight.getHeight() ) ) ); } final BorderCorner bottomLeft = border.getBottomLeft(); if ( isEmptyCorner( bottomLeft ) == false ) { attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "border-bottom-left-x", pointConverter.format( StrictGeomUtility.toExternalValue( bottomLeft.getWidth() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "border-bottom-left-y", pointConverter.format( StrictGeomUtility.toExternalValue( bottomLeft.getHeight() ) ) ); } final BorderCorner bottomRight = border.getBottomRight(); if ( isEmptyCorner( bottomRight ) == false ) { attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "border-bottom-right-x", pointConverter.format( StrictGeomUtility.toExternalValue( bottomRight.getWidth() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "border-bottom-right-y", pointConverter.format( StrictGeomUtility.toExternalValue( bottomRight.getHeight() ) ) ); } if ( sblp.getMarginTop() > 0 ) { attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "margin-top", pointConverter.format( StrictGeomUtility.toExternalValue( sblp.getMarginTop() ) ) ); } if ( sblp.getMarginLeft() > 0 ) { attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "margin-left", pointConverter.format( StrictGeomUtility.toExternalValue( sblp.getMarginLeft() ) ) ); } if ( sblp.getMarginBottom() > 0 ) { attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "margin-bottom", pointConverter.format( StrictGeomUtility.toExternalValue( sblp.getMarginBottom() ) ) ); } if ( sblp.getMarginRight() > 0 ) { attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "margin-right", pointConverter.format( StrictGeomUtility.toExternalValue( sblp.getMarginRight() ) ) ); } if ( definition.getPaddingTop() > 0 ) { attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "padding-top", pointConverter.format( StrictGeomUtility.toExternalValue( definition.getPaddingTop() ) ) ); } if ( definition.getPaddingLeft() > 0 ) { attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "padding-left", pointConverter.format( StrictGeomUtility.toExternalValue( definition.getPaddingLeft() ) ) ); } if ( definition.getPaddingBottom() > 0 ) { attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "padding-bottom", pointConverter.format( StrictGeomUtility.toExternalValue( definition.getPaddingBottom() ) ) ); } if ( definition.getPaddingRight() > 0 ) { attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "padding-right", pointConverter.format( StrictGeomUtility.toExternalValue( definition.getPaddingRight() ) ) ); } attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "x", pointConverter.format( StrictGeomUtility.toExternalValue( box.getX() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "y", pointConverter.format( StrictGeomUtility.toExternalValue( box.getY() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "width", pointConverter.format( StrictGeomUtility.toExternalValue( box.getWidth() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "height", pointConverter.format( StrictGeomUtility.toExternalValue( box.getHeight() ) ) ); final Color backgroundColor = (Color) box.getStyleSheet().getStyleProperty( ElementStyleKeys.BACKGROUND_COLOR ); if ( backgroundColor != null ) { attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "background-color", ColorValueConverter.colorToString( backgroundColor ) ); } final Color color = (Color) box.getStyleSheet().getStyleProperty( ElementStyleKeys.PAINT ); if ( color != null ) { attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "color", ColorValueConverter.colorToString( color ) ); } attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "font-face", (String) box.getStyleSheet().getStyleProperty( TextStyleKeys.FONT ) ); final Object o = box.getStyleSheet().getStyleProperty( TextStyleKeys.FONTSIZE ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "font-size", pointIntConverter.format( o ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "font-style-bold", String.valueOf( box.getStyleSheet().getStyleProperty( TextStyleKeys.BOLD ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "font-style-italics", String.valueOf( box.getStyleSheet().getStyleProperty( TextStyleKeys.ITALIC ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "font-style-underline", String.valueOf( box.getStyleSheet().getStyleProperty( TextStyleKeys.UNDERLINED ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "font-style-strikethrough", String.valueOf( box.getStyleSheet().getStyleProperty( TextStyleKeys.STRIKETHROUGH ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "anchor", (String) box.getStyleSheet().getStyleProperty( ElementStyleKeys.ANCHOR_NAME ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "href", (String) box.getStyleSheet().getStyleProperty( ElementStyleKeys.HREF_TARGET ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "href-window", (String) box.getStyleSheet().getStyleProperty( ElementStyleKeys.HREF_WINDOW ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "href-title", (String) box.getStyleSheet().getStyleProperty( ElementStyleKeys.HREF_TITLE ) ); return attributeList; } private String convertColorToString( final BorderEdge borderEdge ) { final Color o = borderEdge.getColor(); if ( o == null ) { return null; } return ColorValueConverter.colorToString( o ); } private boolean isEmptyCorner( final BorderCorner corner ) { if ( ignoreEmptyBorders == false ) { return false; } return corner.getWidth() == 0 && corner.getHeight() == 0; } protected boolean startBlockBox( final BlockRenderBox box ) { try { final int nodeType = box.getNodeType(); if ( ( nodeType & LayoutNodeTypes.MASK_BOX_PAGEAREA ) == LayoutNodeTypes.MASK_BOX_PAGEAREA ) { final AttributeList list = createBoxAttributeList( box ); list.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "type", box.getName() ); xmlWriter.writeTag( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "page-area", list, XmlWriter.OPEN ); writeElementAttributes( box ); return true; } else if ( nodeType == LayoutNodeTypes.TYPE_BOX_PARAGRAPH ) { return startBox( box, "p" ); } else { return startBox( box, "block" ); } } catch ( IOException e ) { throw new InvalidReportStateException( e.getMessage(), e ); } } protected void finishBlockBox( final BlockRenderBox box ) { finishBox(); } protected boolean startInlineBox( final InlineRenderBox box ) { if ( box.getNodeType() == LayoutNodeTypes.TYPE_BOX_LINEBOX ) { return startBox( box, "line" ); } else { return startBox( box, "inline" ); } } protected void finishInlineBox( final InlineRenderBox box ) { finishBox(); } protected boolean startOtherBox( final RenderBox box ) { return startBox( box, "other-box" ); } protected void finishOtherBox( final RenderBox box ) { finishBox(); } protected boolean startCanvasBox( final CanvasRenderBox box ) { return startBox( box, "canvas" ); } protected void finishCanvasBox( final CanvasRenderBox box ) { finishBox(); } protected void processOtherNode( final RenderNode node ) { try { final int nodeType = node.getNodeType(); if ( nodeType == LayoutNodeTypes.TYPE_NODE_TEXT ) { final RenderableText text = (RenderableText) node; final AttributeList attributeList = new AttributeList(); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "x", pointConverter.format( StrictGeomUtility.toExternalValue( node.getX() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "y", pointConverter.format( StrictGeomUtility.toExternalValue( node.getY() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "width", pointConverter.format( StrictGeomUtility.toExternalValue( node.getWidth() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "height", pointConverter.format( StrictGeomUtility.toExternalValue( node.getHeight() ) ) ); xmlWriter.writeTag( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "text", attributeList, XmlWriter.OPEN ); xmlWriter.writeTextNormalized( text.getRawText(), true ); xmlWriter.writeCloseTag(); } else if ( nodeType == LayoutNodeTypes.TYPE_NODE_COMPLEX_TEXT ) { final RenderableComplexText renderableComplexText = (RenderableComplexText) node; final AttributeList attributeList = new AttributeList(); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "x", pointConverter.format( StrictGeomUtility.toExternalValue( node.getX() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "y", pointConverter.format( StrictGeomUtility.toExternalValue( node.getY() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "width", pointConverter.format( StrictGeomUtility.toExternalValue( node.getWidth() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "height", pointConverter.format( StrictGeomUtility.toExternalValue( node.getHeight() ) ) ); final String text = renderableComplexText.getRawText(); xmlWriter.writeTag( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "text", attributeList, XmlWriter.OPEN ); xmlWriter.writeTextNormalized( text, true ); xmlWriter.writeCloseTag(); } else if ( nodeType == LayoutNodeTypes.TYPE_NODE_SPACER ) { final SpacerRenderNode spacer = (SpacerRenderNode) node; final AttributeList attributeList = new AttributeList(); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "width", pointConverter.format( StrictGeomUtility.toExternalValue( node.getWidth() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "height", pointConverter.format( StrictGeomUtility.toExternalValue( node.getHeight() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "preserve", String.valueOf( spacer.isDiscardable() == false ) ); xmlWriter.writeTag( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "spacer", attributeList, XmlWriter.CLOSE ); } } catch ( IOException e ) { throw new InvalidReportStateException( e.getMessage(), e ); } } protected void processRenderableContent( final RenderableReplacedContentBox node ) { try { final RenderableReplacedContent prc = node.getContent(); final AttributeList attributeList = new AttributeList(); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "x", pointConverter.format( StrictGeomUtility.toExternalValue( node.getX() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "y", pointConverter.format( StrictGeomUtility.toExternalValue( node.getY() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "width", pointConverter.format( StrictGeomUtility.toExternalValue( node.getWidth() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "height", pointConverter.format( StrictGeomUtility.toExternalValue( node.getHeight() ) ) ); attributeList .setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "source", String.valueOf( prc.getSource() ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "content-width", pointConverter.format( StrictGeomUtility.toExternalValue( prc.getContentWidth() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "content-height", pointConverter.format( StrictGeomUtility.toExternalValue( prc.getContentHeight() ) ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "requested-width", convertRenderLength( prc.getRequestedWidth() ) ); attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "requested-height", convertRenderLength( prc.getRequestedHeight() ) ); final Object o = prc.getRawObject(); if ( o != null ) { attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "raw-object-type", o.getClass().getName() ); } else { attributeList.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "raw-object-type", "null" ); } xmlWriter .writeTag( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "replaced-content", attributeList, XmlWriter.OPEN ); writeElementAttributes( node ); xmlWriter.writeCloseTag(); } catch ( IOException e ) { throw new InvalidReportStateException( e.getMessage(), e ); } } private String convertRenderLength( final RenderLength length ) { if ( length == null ) { return null; } if ( RenderLength.AUTO.equals( length ) ) { return "auto"; } if ( length.isPercentage() ) { return pointConverter.format( StrictGeomUtility.toExternalValue( -length.getValue() ) ) + '%'; } return pointConverter.format( StrictGeomUtility.toExternalValue( length.getValue() ) ); } protected void processParagraphChilds( final ParagraphRenderBox box ) { processBoxChilds( box ); } protected boolean startRowBox( final RenderBox box ) { return startBox( box, "row" ); } protected void finishRowBox( final RenderBox box ) { finishBox(); } private void finishBox() { try { xmlWriter.writeCloseTag(); } catch ( IOException e ) { throw new InvalidReportStateException( e.getMessage(), e ); } } protected boolean startTableCellBox( final TableCellRenderBox box ) { try { AttributeList attrs = createBoxAttributeList( box ); attrs.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "col-span", String.valueOf( box.getColSpan() ) ); attrs.setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "row-span", String.valueOf( box.getRowSpan() ) ); attrs .setAttribute( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "col-index", String.valueOf( box.getColumnIndex() ) ); xmlWriter.writeTag( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, "table-cell", attrs, XmlWriter.OPEN ); writeElementAttributes( box ); return true; } catch ( IOException e ) { throw new InvalidReportStateException( e.getMessage(), e ); } } protected void finishTableCellBox( final TableCellRenderBox box ) { finishBox(); } protected boolean startTableRowBox( final TableRowRenderBox box ) { return startBox( box, "table-row" ); } protected void finishTableRowBox( final TableRowRenderBox box ) { finishBox(); } protected boolean startTableSectionBox( final TableSectionRenderBox box ) { return startBox( box, "table-section" ); } protected void finishTableSectionBox( final TableSectionRenderBox box ) { finishBox(); } protected boolean startTableColumnGroupBox( final TableColumnGroupNode box ) { return startBox( box, "colgroup" ); } protected void finishTableColumnGroupBox( final TableColumnGroupNode box ) { finishBox(); } protected boolean startTableBox( final TableRenderBox box ) { return startBox( box, "table" ); } protected void finishTableBox( final TableRenderBox box ) { finishBox(); } protected boolean startAutoBox( final RenderBox box ) { final int layoutNodeType = box.getLayoutNodeType() & LayoutNodeTypes.MASK_BASIC_BOX_TYPE; if ( layoutNodeType == LayoutNodeTypes.MASK_BOX_BLOCK ) { return startBox( box, "block" ); } if ( layoutNodeType == LayoutNodeTypes.MASK_BOX_CANVAS ) { return startBox( box, "canvas" ); } if ( layoutNodeType == LayoutNodeTypes.MASK_BOX_INLINE ) { return startBox( box, "inline" ); } if ( layoutNodeType == LayoutNodeTypes.MASK_BOX_ROW ) { return startBox( box, "row" ); } return startBox( box, "auto" ); } protected void finishAutoBox( final RenderBox box ) { finishBox(); } protected boolean startBox( final RenderBox box, final String tagName ) { try { xmlWriter .writeTag( XmlDocumentWriter.LAYOUT_OUTPUT_NAMESPACE, tagName, createBoxAttributeList( box ), XmlWriter.OPEN ); writeElementAttributes( box ); return true; } catch ( IOException e ) { throw new InvalidReportStateException( e.getMessage(), e ); } } private static class DualKeySorter implements Comparator<AttributeMap.DualKey> { public int compare( final AttributeMap.DualKey o1, final AttributeMap.DualKey o2 ) { if ( o1 == null && o2 == null ) { return 0; } if ( o1 == null ) { return -1; } if ( o2 == null ) { return 1; } int ns = o1.namespace.compareTo( o2.namespace ); if ( ns != 0 ) { return ns; } return o1.name.compareTo( o2.name ); } } }