/*
* 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.table.rtf.helper;
import java.awt.Color;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.engine.classic.core.ClassicEngineInfo;
import org.pentaho.reporting.engine.classic.core.ElementAlignment;
import org.pentaho.reporting.engine.classic.core.InvalidReportStateException;
import org.pentaho.reporting.engine.classic.core.layout.model.BorderEdge;
import org.pentaho.reporting.engine.classic.core.layout.model.LogicalPageBox;
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.output.ContentProcessingException;
import org.pentaho.reporting.engine.classic.core.layout.output.LogicalPageKey;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorFeature;
import org.pentaho.reporting.engine.classic.core.modules.output.table.base.AbstractTableOutputProcessor;
import org.pentaho.reporting.engine.classic.core.modules.output.table.base.CellBackground;
import org.pentaho.reporting.engine.classic.core.modules.output.table.base.CellBackgroundProducer;
import org.pentaho.reporting.engine.classic.core.modules.output.table.base.CellMarker;
import org.pentaho.reporting.engine.classic.core.modules.output.table.base.SheetLayout;
import org.pentaho.reporting.engine.classic.core.modules.output.table.base.TableContentProducer;
import org.pentaho.reporting.engine.classic.core.modules.output.table.rtf.itext.PatchRtfCell;
import org.pentaho.reporting.engine.classic.core.modules.output.table.rtf.itext.PatchRtfWriter2;
import org.pentaho.reporting.engine.classic.core.style.BorderStyle;
import org.pentaho.reporting.engine.classic.core.style.ElementStyleKeys;
import org.pentaho.reporting.engine.classic.core.util.NoCloseOutputStream;
import org.pentaho.reporting.engine.classic.core.util.geom.StrictGeomUtility;
import org.pentaho.reporting.libraries.base.config.Configuration;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
import com.lowagie.text.Cell;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Rectangle;
import com.lowagie.text.Table;
import com.lowagie.text.rtf.table.RtfBorder;
/**
* Creation-Date: 09.05.2007, 14:52:05
*
* @author Thomas Morgner
*/
public class RTFPrinter {
private static final Log logger = LogFactory.getLog( RTFPrinter.class );
private static final String CREATOR = ClassicEngineInfo.getInstance().getName() + " version "
+ ClassicEngineInfo.getInstance().getVersion();
private OutputStream outputStream;
private Configuration config;
private ResourceManager resourceManager;
private RTFImageCache imageCache;
private Document document;
private Table table;
private CellBackgroundProducer cellBackgroundProducer;
public RTFPrinter() {
}
public void init( final Configuration config, final OutputStream outputStream, final ResourceManager resourceManager ) {
this.outputStream = outputStream;
this.config = config;
this.resourceManager = resourceManager;
}
/**
* @noinspection IOResourceOpenedButNotSafelyClosed
*/
public void print( final LogicalPageKey logicalPageKey, final LogicalPageBox logicalPage,
final TableContentProducer contentProducer, final RTFOutputProcessorMetaData metaData, final boolean incremental )
throws ContentProcessingException {
final int startRow = contentProducer.getFinishedRows();
final int finishRow = contentProducer.getFilledRows();
if ( incremental && startRow == finishRow ) {
return;
}
if ( document == null ) {
this.cellBackgroundProducer =
new CellBackgroundProducer( metaData
.isFeatureSupported( AbstractTableOutputProcessor.TREAT_ELLIPSE_AS_RECTANGLE ), metaData
.isFeatureSupported( OutputProcessorFeature.UNALIGNED_PAGEBANDS ) );
final PhysicalPageBox pageFormat = logicalPage.getPageGrid().getPage( 0, 0 );
final float urx = (float) StrictGeomUtility.toExternalValue( pageFormat.getWidth() );
final float ury = (float) StrictGeomUtility.toExternalValue( pageFormat.getHeight() );
final float marginLeft = (float) StrictGeomUtility.toExternalValue( pageFormat.getImageableX() );
final float marginRight =
(float) StrictGeomUtility.toExternalValue( pageFormat.getWidth() - pageFormat.getImageableWidth()
- pageFormat.getImageableX() );
final float marginTop = (float) StrictGeomUtility.toExternalValue( pageFormat.getImageableY() );
final float marginBottom =
(float) StrictGeomUtility.toExternalValue( pageFormat.getHeight() - pageFormat.getImageableHeight()
- pageFormat.getImageableY() );
final Rectangle pageSize = new Rectangle( urx, ury );
document = new Document( pageSize, marginLeft, marginRight, marginTop, marginBottom );
imageCache = new RTFImageCache( resourceManager );
// rtf does not support PageFormats or other meta data...
final PatchRtfWriter2 instance = PatchRtfWriter2.getInstance( document, new NoCloseOutputStream( outputStream ) );
instance.getDocumentSettings().setAlwaysUseUnicode( true );
final String author =
config.getConfigProperty( "org.pentaho.reporting.engine.classic.core.modules.output.table.rtf.Author" );
if ( author != null ) {
document.addAuthor( author );
}
final String title =
config.getConfigProperty( "org.pentaho.reporting.engine.classic.core.modules.output.table.rtf.Title" );
if ( title != null ) {
document.addTitle( title );
}
document.addProducer();
document.addCreator( RTFPrinter.CREATOR );
try {
document.addCreationDate();
} catch ( Exception e ) {
RTFPrinter.logger.debug( "Unable to add creation date. It will have to work without it.", e );
}
document.open();
}
// Start a new page.
try {
final SheetLayout sheetLayout = contentProducer.getSheetLayout();
final int columnCount = contentProducer.getColumnCount();
if ( table == null ) {
final int rowCount = contentProducer.getRowCount();
table = new Table( columnCount, rowCount );
table.setAutoFillEmptyCells( false );
table.setWidth( 100 ); // span the full page..
// and finally the content ..
final float[] cellWidths = new float[columnCount];
for ( int i = 0; i < columnCount; i++ ) {
cellWidths[i] = (float) StrictGeomUtility.toExternalValue( sheetLayout.getCellWidth( i, i + 1 ) );
}
table.setWidths( cellWidths );
}
// logger.debug ("Processing: " + startRow + " " + finishRow + " " + incremental);
for ( int row = startRow; row < finishRow; row++ ) {
for ( short col = 0; col < columnCount; col++ ) {
final RenderBox content = contentProducer.getContent( row, col );
final CellMarker.SectionType sectionType = contentProducer.getSectionType( row, col );
if ( content == null ) {
final RenderBox backgroundBox = contentProducer.getBackground( row, col );
final CellBackground background;
if ( backgroundBox != null ) {
background =
cellBackgroundProducer.getBackgroundForBox( logicalPage, sheetLayout, col, row, 1, 1, true,
sectionType, backgroundBox );
} else {
background =
cellBackgroundProducer.getBackgroundAt( logicalPage, sheetLayout, col, row, true, sectionType );
}
if ( background == null ) {
// An empty cell .. ignore
final PatchRtfCell cell = new PatchRtfCell();
cell.setBorderWidth( 0 );
cell.setMinimumHeight( (float) StrictGeomUtility.toExternalValue( sheetLayout.getRowHeight( row ) ) );
table.addCell( cell, row, col );
continue;
}
// A empty cell with a defined background ..
final PatchRtfCell cell = new PatchRtfCell();
cell.setBorderWidth( 0 );
cell.setMinimumHeight( (float) StrictGeomUtility.toExternalValue( sheetLayout.getRowHeight( row ) ) );
updateCellStyle( cell, background );
table.addCell( cell, row, col );
continue;
}
if ( content.isCommited() == false ) {
throw new InvalidReportStateException( "Uncommited content encountered" );
}
final long contentOffset = contentProducer.getContentOffset( row, col );
final long colPos = sheetLayout.getXPosition( col );
final long rowPos = sheetLayout.getYPosition( row );
if ( content.getX() != colPos || ( content.getY() + contentOffset ) != rowPos ) {
// A spanned cell ..
continue;
}
final int colSpan = sheetLayout.getColSpan( col, content.getX() + content.getWidth() );
final int rowSpan = sheetLayout.getRowSpan( row, content.getY() + content.getHeight() + contentOffset );
final CellBackground realBackground =
cellBackgroundProducer.getBackgroundForBox( logicalPage, sheetLayout, col, row, colSpan, rowSpan, false,
sectionType, content );
final PatchRtfCell cell = new PatchRtfCell();
cell.setRowspan( rowSpan );
cell.setColspan( colSpan );
cell.setBorderWidth( 0 );
cell.setMinimumHeight( (float) StrictGeomUtility.toExternalValue( sheetLayout.getRowHeight( row ) ) );
if ( realBackground != null ) {
updateCellStyle( cell, realBackground );
}
computeCellStyle( content, cell );
// export the cell and all content ..
final RTFTextExtractor etx = new RTFTextExtractor( metaData );
etx.compute( content, cell, imageCache );
table.addCell( cell, row, col );
content.setFinishedTable( true );
// logger.debug("set Finished to cell (" + col + ", " + row + "," + content.getName() + ")");
}
}
if ( incremental == false ) {
document.add( table );
table = null;
}
} catch ( DocumentException e ) {
throw new ContentProcessingException( "Failed to generate RTF-Document", e );
}
}
private void computeCellStyle( final RenderBox content, final Cell cell ) {
final ElementAlignment verticalAlign = content.getNodeLayoutProperties().getVerticalAlignment();
if ( ElementAlignment.BOTTOM.equals( verticalAlign ) ) {
cell.setVerticalAlignment( Element.ALIGN_BOTTOM );
} else if ( ElementAlignment.MIDDLE.equals( verticalAlign ) ) {
cell.setVerticalAlignment( Element.ALIGN_MIDDLE );
} else {
cell.setVerticalAlignment( Element.ALIGN_TOP );
}
final ElementAlignment textAlign =
(ElementAlignment) content.getStyleSheet().getStyleProperty( ElementStyleKeys.ALIGNMENT );
if ( ElementAlignment.RIGHT.equals( textAlign ) ) {
cell.setHorizontalAlignment( Element.ALIGN_RIGHT );
} else if ( ElementAlignment.JUSTIFY.equals( textAlign ) ) {
cell.setHorizontalAlignment( Element.ALIGN_JUSTIFIED );
} else if ( ElementAlignment.CENTER.equals( textAlign ) ) {
cell.setHorizontalAlignment( Element.ALIGN_CENTER );
} else {
cell.setHorizontalAlignment( Element.ALIGN_LEFT );
}
}
public void close() {
if ( document != null ) {
// cleanup..
document.close();
try {
outputStream.flush();
} catch ( IOException e ) {
RTFPrinter.logger.info( "Failed to flush the RTF-Output stream." );
} finally {
document = null;
}
}
}
private int translateBorderStyle( final BorderStyle borderStyle ) {
if ( BorderStyle.DASHED.equals( borderStyle ) ) {
return RtfBorder.BORDER_DASHED;
}
if ( BorderStyle.DOT_DASH.equals( borderStyle ) ) {
return RtfBorder.BORDER_DOT_DASH;
}
if ( BorderStyle.DOT_DOT_DASH.equals( borderStyle ) ) {
return RtfBorder.BORDER_DOT_DOT_DASH;
}
if ( BorderStyle.DOTTED.equals( borderStyle ) ) {
return RtfBorder.BORDER_DOTTED;
}
if ( BorderStyle.DOUBLE.equals( borderStyle ) ) {
return RtfBorder.BORDER_DOUBLE;
}
if ( BorderStyle.HIDDEN.equals( borderStyle ) ) {
return RtfBorder.BORDER_NONE;
}
if ( BorderStyle.NONE.equals( borderStyle ) ) {
return RtfBorder.BORDER_NONE;
}
if ( BorderStyle.GROOVE.equals( borderStyle ) ) {
return RtfBorder.BORDER_ENGRAVE;
}
if ( BorderStyle.RIDGE.equals( borderStyle ) ) {
return RtfBorder.BORDER_EMBOSS;
}
if ( BorderStyle.INSET.equals( borderStyle ) ) {
return RtfBorder.BORDER_SINGLE;
}
if ( BorderStyle.OUTSET.equals( borderStyle ) ) {
return RtfBorder.BORDER_SINGLE;
}
if ( BorderStyle.SOLID.equals( borderStyle ) ) {
return RtfBorder.BORDER_SINGLE;
}
if ( BorderStyle.WAVE.equals( borderStyle ) ) {
return RtfBorder.BORDER_WAVY;
}
return RtfBorder.BORDER_NONE;
}
private void updateCellStyle( final Cell cell, final CellBackground background ) {
final Color backgroundColor = background.getBackgroundColor();
if ( backgroundColor != null ) {
cell.setBackgroundColor( backgroundColor );
}
final BorderEdge top = background.getTop();
if ( BorderEdge.EMPTY.equals( top ) == false ) {
cell.setBorderColorTop( top.getColor() );
cell.setBorderWidthTop( (float) StrictGeomUtility.toExternalValue( top.getWidth() ) );
}
final BorderEdge left = background.getLeft();
if ( BorderEdge.EMPTY.equals( left ) == false ) {
cell.setBorderColorLeft( left.getColor() );
cell.setBorderWidthLeft( (float) StrictGeomUtility.toExternalValue( left.getWidth() ) );
}
final BorderEdge bottom = background.getBottom();
if ( BorderEdge.EMPTY.equals( bottom ) == false ) {
cell.setBorderColorBottom( bottom.getColor() );
cell.setBorderWidthBottom( (float) StrictGeomUtility.toExternalValue( bottom.getWidth() ) );
}
final BorderEdge right = background.getRight();
if ( BorderEdge.EMPTY.equals( right ) == false ) {
cell.setBorderColorRight( right.getColor() );
cell.setBorderWidthRight( (float) StrictGeomUtility.toExternalValue( right.getWidth() ) );
}
}
}