/*
* 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 - 2009 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 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.RtfWriter2;
import com.lowagie.text.rtf.table.RtfBorder;
import com.lowagie.text.rtf.table.RtfCell;
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.SheetLayout;
import org.pentaho.reporting.engine.classic.core.modules.output.table.base.TableContentProducer;
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;
/**
* 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
{
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 RtfWriter2 instance = RtfWriter2.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);
}
final int startRow = contentProducer.getFinishedRows();
final int finishRow = contentProducer.getFilledRows();
//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 int sectionType = contentProducer.getSectionType(row, col);
if (content == null)
{
final CellBackground background = cellBackgroundProducer.getBackgroundAt
(logicalPage, sheetLayout, col, row, false, sectionType);
if (background == null)
{
// An empty cell .. ignore
final RtfCell cell = new RtfCell();
cell.setBorderWidth(0);
table.addCell(cell, row, col);
continue;
}
// A empty cell with a defined background ..
final RtfCell cell = new RtfCell();
cell.setBorderWidth(0);
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.getBackgroundAt
(logicalPage, sheetLayout, col, row, colSpan, rowSpan, false, sectionType);
final Cell cell = new Cell();
cell.setRowspan(rowSpan);
cell.setColspan(colSpan);
cell.setBorderWidth(0);
// Sadly RTF-Cell does not provide a setter for the row-span. Therefore we have to "import" a plain cell
// for that. The design of the iText RTF writer seems to be seriously flawed here ..
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.setFinished(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.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()));
}
}
}