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