/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2006 - 2016 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.modules.output.fast.xls;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.pentaho.reporting.engine.classic.core.AttributeNames;
import org.pentaho.reporting.engine.classic.core.Band;
import org.pentaho.reporting.engine.classic.core.DefaultImageReference;
import org.pentaho.reporting.engine.classic.core.ImageContainer;
import org.pentaho.reporting.engine.classic.core.PageDefinition;
import org.pentaho.reporting.engine.classic.core.ReportDefinition;
import org.pentaho.reporting.engine.classic.core.ReportElement;
import org.pentaho.reporting.engine.classic.core.function.ExpressionRuntime;
import org.pentaho.reporting.engine.classic.core.layout.model.PhysicalPageBox;
import org.pentaho.reporting.engine.classic.core.layout.output.ContentProcessingException;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorMetaData;
import org.pentaho.reporting.engine.classic.core.layout.output.RenderUtility;
import org.pentaho.reporting.engine.classic.core.modules.output.fast.template.CellLayoutInfo;
import org.pentaho.reporting.engine.classic.core.modules.output.fast.template.FastSheetLayout;
import org.pentaho.reporting.engine.classic.core.modules.output.fast.template.SheetPropertyCollector;
import org.pentaho.reporting.engine.classic.core.modules.output.table.base.CellBackground;
import org.pentaho.reporting.engine.classic.core.modules.output.table.base.SheetLayout;
import org.pentaho.reporting.engine.classic.core.modules.output.table.base.TableRectangle;
import org.pentaho.reporting.engine.classic.core.modules.output.table.xls.helper.CellStyleProducer;
import org.pentaho.reporting.engine.classic.core.modules.output.table.xls.helper.ExcelPrinterBase;
import org.pentaho.reporting.engine.classic.core.style.BandStyleKeys;
import org.pentaho.reporting.engine.classic.core.style.ElementStyleKeys;
import org.pentaho.reporting.engine.classic.core.style.StyleSheet;
import org.pentaho.reporting.engine.classic.core.util.RotatedTextDrawable;
import org.pentaho.reporting.engine.classic.core.util.geom.StrictBounds;
import org.pentaho.reporting.engine.classic.core.util.geom.StrictGeomUtility;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
import org.pentaho.reporting.libraries.resourceloader.factory.drawable.DrawableWrapper;
import java.awt.Image;
import java.awt.Shape;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
@SuppressWarnings( "HardCodedStringLiteral" )
public class FastExcelPrinter extends ExcelPrinterBase {
private static final Log logger = LogFactory.getLog( FastExcelPrinter.class );
private Workbook workbook;
private Sheet sheet;
private PageDefinition pageDefinition;
private int rowOffset;
private long rowHeightOffset;
private FastExcelTextExtractor textExtractor;
private FastSheetLayout sheetLayout;
private long[] cellHeights;
public FastExcelPrinter( final SheetLayout sheetLayout ) {
this.sheetLayout = new FastSheetLayout( sheetLayout );
}
public void init( final OutputProcessorMetaData metaData, final ResourceManager resourceManager,
final ReportDefinition report ) {
this.pageDefinition = report.getPageDefinition();
super.init( metaData, resourceManager );
workbook = createWorkbook();
initializeStyleProducers( workbook );
textExtractor =
new FastExcelTextExtractor( getColorProducer(), getCellStyleProducer().getFontFactory(), workbook
.getCreationHelper() );
}
protected Sheet getSheet() {
return sheet;
}
public Workbook getWorkbook() {
return workbook;
}
public void startSection( final Band band, final long[] cellHeights ) {
this.cellHeights = cellHeights;
this.sheetLayout.reinit( rowHeightOffset, cellHeights );
if ( band.getComputedStyle().getBooleanStyleProperty( BandStyleKeys.PAGEBREAK_BEFORE ) ) {
closeSheet();
}
if ( cellHeights.length > 0 && sheet == null ) {
SheetPropertyCollector collector = new SheetPropertyCollector();
String sheetName = collector.compute( band );
sheet = openSheet( sheetName );
configureSheetColumnWidths( sheet, sheetLayout, sheetLayout.getColumnCount() );
configureSheetPaperSize( sheet, new PhysicalPageBox( pageDefinition.getPageFormat( 0 ), 0, 0 ) );
configureSheetProperties( sheet, collector );
rowOffset = 0;
}
for ( int r = 0; r < cellHeights.length; r += 1 ) {
getRowAt( r + rowOffset ).setHeightInPoints( (float) StrictGeomUtility.toExternalValue( cellHeights[r] ) );
}
}
public void endSection( final Band band, final ArrayList<CellLayoutInfo> backgroundCells ) {
for ( final CellLayoutInfo layoutInfo : backgroundCells ) {
int col = layoutInfo.getX1();
int row = layoutInfo.getY1() + rowOffset;
final Cell cell = getCellAt( col, row );
final CellStyle style = getCellStyleProducer().createCellStyle( null, null, layoutInfo.getBackground() );
if ( style != null ) {
cell.setCellStyle( style );
}
}
if ( band.getComputedStyle().getBooleanStyleProperty( BandStyleKeys.PAGEBREAK_AFTER ) ) {
closeSheet();
}
this.rowOffset += cellHeights.length;
for ( int i = 0; i < cellHeights.length; i++ ) {
this.rowHeightOffset += cellHeights[i];
}
}
protected CellStyleProducer createCellStyleProducer( final Workbook workbook ) {
return new FastExcelCellStyleProducer( super.createCellStyleProducer( workbook ) );
}
public void closeSheet() {
sheet = null;
}
public void print( final CellLayoutInfo tableRectangle, final ReportElement element, final ExpressionRuntime runtime )
throws ContentProcessingException {
TableRectangle rect = new TableRectangle();
rect.setRect( tableRectangle.getX1(), tableRectangle.getY1() + rowOffset, tableRectangle.getX2(), tableRectangle
.getY2()
+ rowOffset );
Cell cellAt = getCellAt( rect.getX1(), rect.getY1() );
CellBackground bg = tableRectangle.getBackground();
CellStyle cellStyle =
getCellStyleProducer().createCellStyle( element.getObjectID(), element.getComputedStyle(), bg );
if ( cellStyle != null ) {
cellAt.setCellStyle( cellStyle );
}
if ( applyCellValue( element, cellAt, rect, runtime ) ) {
mergeCellRegion( rect, cellStyle );
}
}
private void mergeCellRegion( final TableRectangle rectangle, final CellStyle spannedStyle ) {
final int rowSpan = rectangle.getRowSpan();
final int columnSpan = rectangle.getColumnSpan();
if ( rowSpan <= 1 && columnSpan <= 1 ) {
return;
}
int row = rectangle.getY1();
int col = rectangle.getX1();
sheet.addMergedRegion( new CellRangeAddress( row, ( row + rowSpan - 1 ), col, ( col + columnSpan - 1 ) ) );
for ( int spannedRow = 0; spannedRow < rowSpan; spannedRow += 1 ) {
for ( int spannedCol = 0; spannedCol < columnSpan; spannedCol += 1 ) {
final Cell regionCell = getCellAt( ( col + spannedCol ), row + spannedRow );
if ( spannedStyle != null ) {
regionCell.setCellStyle( spannedStyle );
}
}
}
}
/**
* Applies the cell value and determines whether the cell should be merged. Merging will only take place if the cell
* has a row or colspan greater than one. Images will never be merged, as image content is rendered into an anchored
* frame on top of the cells.
*
* @return true, if the cell may to be put into a merged region, false otherwise.
*/
private boolean applyCellValue( final ReportElement content, final Cell cell, final TableRectangle rectangle,
final ExpressionRuntime runtime ) throws ContentProcessingException {
final Object value = textExtractor.compute( content, runtime );
if ( handleImageValues( content, rectangle, value ) ) {
return false;
}
final String linkTarget = (String) content.getComputedStyle().getStyleProperty( ElementStyleKeys.HREF_TARGET );
if ( linkTarget != null ) {
// this may be wrong if we have quotes inside. We should escape them ..
final RotatedTextDrawable extracted = RotatedTextDrawable.extract( value );
final String linkText = extracted == null ? textExtractor.getText() : extracted.getText();
final String formula =
"HYPERLINK(" + splitAndQuoteExcelFormula( linkTarget ) + ","
+ splitAndQuoteExcelFormula( linkText ) + ")";
if ( formula.length() < 1024 ) {
cell.setCellFormula( formula );
return true;
}
logger
.warn( "Excel-Cells cannot contain formulas longer than 1023 characters. Converting hyperlink into plain text" );
}
final Object attr1 =
content.getAttributes().getAttribute( AttributeNames.Excel.NAMESPACE, AttributeNames.Excel.FIELD_FORMULA );
if ( attr1 != null ) {
final String formula = String.valueOf( attr1 );
if ( formula.length() < 1024 ) {
cell.setCellFormula( formula );
return true;
}
logger
.warn( "Excel-Cells cannot contain formulas longer than 1023 characters. Converting excel formula into plain text" );
}
handleValueType( cell, value, workbook );
return true;
}
private boolean handleImageValues( final ReportElement content, final TableRectangle rectangle, final Object value ) {
final StyleSheet rawSource = content.getComputedStyle();
if ( RotatedTextDrawable.extract( value ) != null ) {
return false;
}
if ( value instanceof Image ) {
try {
final StrictBounds contentBounds = sheetLayout.getBounds( rectangle );
final ImageContainer imageContainer = new DefaultImageReference( (Image) value );
createImageCell( rawSource, imageContainer, sheetLayout, rectangle, contentBounds );
} catch ( final IOException ioe ) {
// Should not happen.
logger.warn( "Failed to process AWT-Image in Excel-Export", ioe );
}
return true;
} else if ( value instanceof ImageContainer ) {
final ImageContainer imageContainer = (ImageContainer) value;
final StrictBounds contentBounds = sheetLayout.getBounds( rectangle );
createImageCell( rawSource, imageContainer, sheetLayout, rectangle, contentBounds );
return true;
} else if ( value instanceof DrawableWrapper ) {
final DrawableWrapper drawable = (DrawableWrapper) value;
final StrictBounds contentBounds = sheetLayout.getBounds( rectangle );
final ImageContainer imageFromDrawable =
RenderUtility.createImageFromDrawable( drawable, contentBounds, content.getComputedStyle(), getMetaData() );
createImageCell( rawSource, imageFromDrawable, sheetLayout, rectangle, contentBounds );
return true;
} else if ( value instanceof Shape ) {
// We *could* do this as well ... but for now we dont.
return true;
}
return false;
}
public void closeWorkbook( final OutputStream outputStream ) throws IOException {
workbook.write( outputStream );
}
}