uk.co.spudsoft.birt.emitters.excel.handlers.CellContentHandler Maven / Gradle / Ivy
/*************************************************************************************
* Copyright (c) 2011, 2012, 2013 James Talbut.
* [email protected]
*
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* https://www.eclipse.org/legal/epl-2.0/.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* James Talbut - Initial implementation.
************************************************************************************/
package uk.co.spudsoft.birt.emitters.excel.handlers;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.apache.poi.common.usermodel.HyperlinkType;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.Hyperlink;
import org.apache.poi.ss.usermodel.RichTextString;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.eclipse.birt.core.exception.BirtException;
import org.eclipse.birt.report.engine.api.IHTMLActionHandler;
import org.eclipse.birt.report.engine.api.impl.Action;
import org.eclipse.birt.report.engine.content.ICellContent;
import org.eclipse.birt.report.engine.content.IContent;
import org.eclipse.birt.report.engine.content.IForeignContent;
import org.eclipse.birt.report.engine.content.IHyperlinkAction;
import org.eclipse.birt.report.engine.content.IImageContent;
import org.eclipse.birt.report.engine.content.IStyle;
import org.eclipse.birt.report.engine.css.engine.StyleConstants;
import org.eclipse.birt.report.engine.css.engine.value.StringValue;
import org.eclipse.birt.report.engine.css.engine.value.css.CSSConstants;
import org.eclipse.birt.report.engine.emitter.IContentEmitter;
import org.eclipse.birt.report.engine.ir.DimensionType;
import org.eclipse.birt.report.engine.layout.emitter.Image;
import org.eclipse.birt.report.engine.presentation.ContentEmitterVisitor;
import org.eclipse.birt.report.engine.util.SvgFile;
import org.w3c.dom.css.CSSPrimitiveValue;
import org.w3c.dom.css.CSSValue;
import uk.co.spudsoft.birt.emitters.excel.Area;
import uk.co.spudsoft.birt.emitters.excel.AreaBorders;
import uk.co.spudsoft.birt.emitters.excel.BirtStyle;
import uk.co.spudsoft.birt.emitters.excel.CellImage;
import uk.co.spudsoft.birt.emitters.excel.ClientAnchorConversions;
import uk.co.spudsoft.birt.emitters.excel.Coordinate;
import uk.co.spudsoft.birt.emitters.excel.EmitterServices;
import uk.co.spudsoft.birt.emitters.excel.ExcelEmitter;
import uk.co.spudsoft.birt.emitters.excel.HandlerState;
import uk.co.spudsoft.birt.emitters.excel.RichTextRun;
import uk.co.spudsoft.birt.emitters.excel.StyleManager;
import uk.co.spudsoft.birt.emitters.excel.StyleManagerUtils;
import uk.co.spudsoft.birt.emitters.excel.framework.Logger;
/**
* Cell content handler
*
* @since 3.3
*
*/
public class CellContentHandler extends AbstractHandler {
/**
* Number of milliseconds in a day, to determine whether a given date is
* date/time/datetime
*/
private static final long oneDay = 24 * 60 * 60 * 1000;
/**
* The last value added to a cell
*/
protected Object lastValue;
/**
* The last value added to a cell
*/
protected String lastFormula;
/**
* The BIRT element that provided the lastValue
*/
protected IContent lastElement;
/**
* List of font changes for a single cell.
*/
protected List richTextRuns = new ArrayList();
/**
* When having to join multiple text blocks together, track whether they are
* block or inline display
*/
protected boolean lastCellContentsWasBlock;
/**
* When having to join multiple text blocks together, track whether they need
* more of a gap between them (basically used for flattened table cells)
*/
protected boolean lastCellContentsRequiresSpace;
/**
* The span of the current cell
*/
protected int colSpan;
/**
* Visitor to enable processing of child elements created for foreign (HTML)
* elements.
*/
protected ContentEmitterVisitor contentVisitor;
/**
* Override the cell alignment to this instead, unless zero
*/
protected CSSValue preferredAlignment;
/**
* URL that this cell should hyperlink to
*/
protected String hyperlinkUrl;
/**
* Bookmark that this cell should hyperlink to
*/
protected String hyperlinkBookmark;
private static final String URL_IMAGE_TYPE_SVG = "image/svg+xml";
private static final String URL_PROTOCOL_TYPE_FILE = "file:";
private static final String URL_PROTOCOL_TYPE_DATA = "data:";
private static final String URL_PROTOCOL_TYPE_DATA_BASE = ";base64,";
private static final String URL_PROTOCOL_TYPE_DATA_UTF8 = ";utf8,";
private static final String URL_PROTOCOL_URL_ENCODED_SPACE = "%20";
private static final String STYLE_OVERLAY_DEFAULT_UNIT = "mm";
/**
* Constructor
*
* @param emitter content emitter
* @param log log object
* @param parent parent handler
* @param cell cell content
*/
public CellContentHandler(IContentEmitter emitter, Logger log, IHandler parent, ICellContent cell) {
super(log, parent, cell);
contentVisitor = new ContentEmitterVisitor(emitter);
colSpan = 1;
}
@Override
public void startCell(HandlerState state, ICellContent cell) throws BirtException {
if (cell.getBookmark() != null) {
System.err.println("Bookmark: " + cell.getBookmark());
}
}
/**
* Finish processing for the current (real) cell.
*
* @param element The element that signifies the end of the cell (this may not
* be an ICellContent object if the cell is created for a label
* or text outside of a table).
*/
protected void endCellContent(HandlerState state, ICellContent birtCell, IContent element, Cell cell, Area area) {
StyleManager sm = state.getSm();
StyleManagerUtils smu = state.getSmu();
BirtStyle birtCellStyle = null;
boolean isStyleExactCopy = false;
if (birtCell != null) {
birtCellStyle = new BirtStyle(birtCell);
if (element != null) {
// log.debug( "Overlaying style from ", element );
birtCellStyle.overlay(element);
}
} else if (element != null) {
birtCellStyle = new BirtStyle(element);
isStyleExactCopy = true;
} else {
birtCellStyle = new BirtStyle(state.getSm().getCssEngine());
}
if (preferredAlignment != null) {
birtCellStyle.setProperty(StyleConstants.STYLE_TEXT_ALIGN, preferredAlignment);
}
if (CSSConstants.CSS_TRANSPARENT_VALUE.equals(birtCellStyle.getString(StyleConstants.STYLE_BACKGROUND_COLOR))) {
if (parent != null) {
birtCellStyle.setProperty(StyleConstants.STYLE_BACKGROUND_COLOR, parent.getBackgroundColour());
}
}
birtCellStyle.setTextIndentInUse(EmitterServices.booleanOption(state.getRenderOptions(), element,
ExcelEmitter.DISPLAY_TEXT_INDENT, true));
birtCellStyle.setTextIndentMode(EmitterServices.stringOption(state.getRenderOptions(), element,
ExcelEmitter.TEXT_INDENT_MODE, ExcelEmitter.TEXT_INDENT_MODE_SPACING_ALL));
if (hyperlinkUrl != null) {
Hyperlink hyperlink = cell.getSheet().getWorkbook().getCreationHelper()
.createHyperlink(HyperlinkType.URL);
hyperlink.setAddress(hyperlinkUrl);
cell.setHyperlink(hyperlink);
birtCellStyle.setProperty(StyleConstants.STYLE_COLOR,
new StringValue(CSSPrimitiveValue.CSS_STRING, CSSConstants.CSS_BLUE_VALUE));
birtCellStyle.setProperty(StyleConstants.STYLE_TEXT_UNDERLINE,
new StringValue(CSSPrimitiveValue.CSS_STRING, CSSConstants.CSS_UNDERLINE_VALUE));
}
if (hyperlinkBookmark != null) {
Hyperlink hyperlink = cell.getSheet().getWorkbook().getCreationHelper()
.createHyperlink(HyperlinkType.DOCUMENT);
hyperlink.setAddress(prepareName(hyperlinkBookmark));
cell.setHyperlink(hyperlink);
birtCellStyle.setProperty(StyleConstants.STYLE_COLOR,
new StringValue(CSSPrimitiveValue.CSS_STRING, CSSConstants.CSS_BLUE_VALUE));
birtCellStyle.setProperty(StyleConstants.STYLE_TEXT_UNDERLINE,
new StringValue(CSSPrimitiveValue.CSS_STRING, CSSConstants.CSS_UNDERLINE_VALUE));
}
if ((lastFormula != null) && (lastValue == null)) {
setCellContents(cell, null, lastFormula);
}
if (lastValue != null) {
if (lastValue instanceof String) {
String lastString = (String) lastValue;
smu.correctFontColorIfBackground(birtCellStyle);
for (RichTextRun run : richTextRuns) {
run.font = smu.correctFontColorIfBackground(sm.getFontManager(), state.getWb(), birtCellStyle,
run.font);
}
if (!richTextRuns.isEmpty()) {
RichTextString rich = smu.createRichTextString(lastString);
int runStart = richTextRuns.get(0).startIndex;
Font lastFont = richTextRuns.get(0).font;
for (int i = 0; i < richTextRuns.size(); ++i) {
RichTextRun run = richTextRuns.get(i);
log.debug("Run: ", run.startIndex, " font :", run.font);
if (!lastFont.equals(run.font)) {
log.debug("Applying ", runStart, " - ", run.startIndex);
rich.applyFont(runStart, run.startIndex, lastFont);
runStart = run.startIndex;
lastFont = richTextRuns.get(i).font;
}
}
log.debug("Finalising with ", runStart, " - ", lastString.length());
rich.applyFont(runStart, lastString.length(), lastFont);
setCellContents(cell, rich, lastFormula);
} else {
setCellContents(cell, lastString, lastFormula);
}
if (birtCell != null) {
if (lastString.contains("\n")) {
if (!CSSConstants.CSS_NOWRAP_VALUE.equals(lastElement.getStyle().getWhiteSpace())) {
birtCellStyle.setProperty(StyleConstants.STYLE_WHITE_SPACE,
new StringValue(CSSPrimitiveValue.CSS_STRING, CSSConstants.CSS_PRE_VALUE));
}
}
if (!richTextRuns.isEmpty()) {
birtCellStyle.setProperty(StyleConstants.STYLE_VERTICAL_ALIGN,
new StringValue(CSSPrimitiveValue.CSS_STRING, CSSConstants.CSS_TOP_VALUE));
}
if (preferredAlignment != null) {
birtCellStyle.setProperty(StyleConstants.STYLE_TEXT_ALIGN, preferredAlignment);
}
}
} else {
setCellContents(cell, lastValue, lastFormula);
}
}
if (birtCell != null && birtCell.getDiagonalNumber() >= 1
&& !"none".equalsIgnoreCase(birtCell.getDiagonalStyle())) {
String diagonalWidth = null;
if (birtCell.getDiagonalWidth() != null) {
diagonalWidth = birtCell.getDiagonalWidth().toString();
}
birtCellStyle.setProperty(StyleConstants.STYLE_BORDER_DIAGONAL_WIDTH,
new StringValue(CSSPrimitiveValue.CSS_STRING, diagonalWidth));
birtCellStyle.setProperty(StyleConstants.STYLE_BORDER_DIAGONAL_COLOR,
new StringValue(CSSPrimitiveValue.CSS_STRING, birtCell.getDiagonalColor()));
birtCellStyle.setProperty(StyleConstants.STYLE_BORDER_DIAGONAL_STYLE,
new StringValue(CSSPrimitiveValue.CSS_STRING, birtCell.getDiagonalStyle()));
}
if (birtCell != null && birtCell.getAntidiagonalNumber() >= 1
&& !"none".equalsIgnoreCase(birtCell.getAntidiagonalStyle())) {
String antidiagonalWidth = null;
if (birtCell.getAntidiagonalWidth() != null) {
antidiagonalWidth = birtCell.getAntidiagonalWidth().toString();
}
birtCellStyle.setProperty(StyleConstants.STYLE_BORDER_ANTIDIAGONAL_WIDTH,
new StringValue(CSSPrimitiveValue.CSS_STRING, antidiagonalWidth));
birtCellStyle.setProperty(StyleConstants.STYLE_BORDER_ANTIDIAGONAL_COLOR,
new StringValue(CSSPrimitiveValue.CSS_STRING, birtCell.getAntidiagonalColor()));
birtCellStyle.setProperty(StyleConstants.STYLE_BORDER_ANTIDIAGONAL_STYLE,
new StringValue(CSSPrimitiveValue.CSS_STRING, birtCell.getAntidiagonalStyle()));
}
int colIndex = cell.getColumnIndex();
if (birtCellStyle != null) {
state.getSmu().applyAreaBordersToCell(state.areaBorders, cell, birtCellStyle, state.rowNum, colIndex);
}
if ((birtCell != null) && ((birtCell.getColSpan() > 1) || (birtCell.getRowSpan() > 1))) {
AreaBorders mergedRegionBorders = AreaBorders.createForMergedCells(state.rowNum + birtCell.getRowSpan() - 1,
colIndex, colIndex + birtCell.getColSpan() - 1, state.rowNum, 1, 1, birtCellStyle);
if (mergedRegionBorders != null) {
state.insertBorderOverload(mergedRegionBorders);
}
}
if (birtCellStyle != null) {
String customNumberFormat = EmitterServices.stringOption(state.getRenderOptions(), element,
ExcelEmitter.CUSTOM_NUMBER_FORMAT, null);
if (customNumberFormat != null) {
StyleManagerUtils.setNumberFormat(birtCellStyle, ExcelEmitter.CUSTOM_NUMBER_FORMAT + customNumberFormat,
null);
}
overlayBirtCellStyleSpacing(smu, birtCellStyle, element, isStyleExactCopy);
setCellStyle(sm, cell, birtCellStyle, lastValue);
}
// Excel auto calculates the row height (if it isn't specified) as long as the
// cell isn't merged - if it is merged I have to do it
if (((colSpan > 1) || (state.rowHasSpans(state.rowNum)))
&& ((lastValue instanceof String) || (lastValue instanceof RichTextString))) {
int spannedRowAlgorithm = EmitterServices.integerOption(state.getRenderOptions(), element,
ExcelEmitter.SPANNED_ROW_HEIGHT, ExcelEmitter.SPANNED_ROW_HEIGHT_SPREAD);
Font defaultFont = state.getWb().getFontAt(cell.getCellStyle().getFontIndexAsInt() /* .getFontIndex() */);
double cellWidth = spanWidthMillimetres(state.currentSheet, cell.getColumnIndex(),
cell.getColumnIndex() + colSpan - 1);
float cellDesiredHeight = smu.calculateTextHeightPoints(cell.getStringCellValue(), defaultFont, cellWidth,
richTextRuns, cell.getCellStyle().getWrapText());
if (cellDesiredHeight > state.requiredRowHeightInPoints) {
int rowSpan = birtCell.getRowSpan();
if (rowSpan < 2) {
state.requiredRowHeightInPoints = cellDesiredHeight;
} else {
switch (spannedRowAlgorithm) {
case ExcelEmitter.SPANNED_ROW_HEIGHT_FIRST:
state.requiredRowHeightInPoints = cellDesiredHeight;
break;
case ExcelEmitter.SPANNED_ROW_HEIGHT_IGNORED:
break;
default:
if (area != null) {
area.setHeight(cellDesiredHeight);
}
}
}
}
}
// Adjust the required row height for any relevant areas based on what's left
float rowSpanHeightRequirement = state.calculateRowSpanHeightRequirement(state.rowNum);
if (rowSpanHeightRequirement > state.requiredRowHeightInPoints) {
state.requiredRowHeightInPoints = rowSpanHeightRequirement;
}
if (EmitterServices.booleanOption(state.getRenderOptions(), element, ExcelEmitter.FREEZE_PANES, false)) {
if (state.currentSheet.getPaneInformation() == null) {
state.currentSheet.createFreezePane(state.colNum, state.rowNum);
}
}
lastValue = null;
lastFormula = null;
lastElement = null;
richTextRuns.clear();
}
/*
* Overlay to set the margin and padding spacing for the BirtStyle of the excel
* cell
*/
private BirtStyle overlayBirtCellStyleSpacing(StyleManagerUtils smu, BirtStyle birtCellStyle, IContent element,
boolean isStyleExactCopy) {
// Overlay of spacing
if (element != null && element.getStyle() != null) {
IStyle elemStyle = element.getStyle();
IContent fc = null;
double paddingLeftValueElementContent = 0.0;
double paddingRightValueElementContent = 0.0;
// Computed style: values of html-typed elements won't be inherited
// Foreign content: original container of the html-elements
try {
fc = (IContent) element.getParent().getParent();
} catch (Exception e) {
/* do nothing */
}
if (fc != null && fc instanceof IForeignContent && fc.getStyle() != null) {
elemStyle = fc.getStyle();
}
// Padding calculation: cell content and element content
// Avoid the element usage if the birtCellStyle is a copy of the element
if (!isStyleExactCopy) {
// Element content: padding left/right of the cell
String paddingLeftElementContent = (elemStyle.getProperty(StyleConstants.STYLE_PADDING_LEFT) != null)
? elemStyle.getProperty(StyleConstants.STYLE_PADDING_LEFT).getCssText()
: ("0" + STYLE_OVERLAY_DEFAULT_UNIT);
String paddingRightElementContent = (elemStyle.getProperty(StyleConstants.STYLE_PADDING_RIGHT) != null)
? elemStyle.getProperty(StyleConstants.STYLE_PADDING_RIGHT).getCssText()
: ("0" + STYLE_OVERLAY_DEFAULT_UNIT);
// Element padding values
paddingLeftValueElementContent = smu
.convertDimensionToMillimetres(DimensionType.parserUnit(paddingLeftElementContent));
paddingRightValueElementContent = smu
.convertDimensionToMillimetres(DimensionType.parserUnit(paddingRightElementContent));
}
// Element content: margin left/right of the element
CSSValue marginLeftElementContent = elemStyle.getProperty(StyleConstants.STYLE_MARGIN_LEFT);
CSSValue marginRightElementContent = elemStyle.getProperty(StyleConstants.STYLE_MARGIN_RIGHT);
// Calculation the sum of the cell & element padding
// Cell content: padding left/right of the cell
String paddingLeftCellContent = (birtCellStyle.getProperty(StyleConstants.STYLE_PADDING_LEFT) != null)
? birtCellStyle.getProperty(StyleConstants.STYLE_PADDING_LEFT).getCssText()
: ("0" + STYLE_OVERLAY_DEFAULT_UNIT);
String paddingRightCellContent = (birtCellStyle.getProperty(StyleConstants.STYLE_PADDING_RIGHT) != null)
? birtCellStyle.getProperty(StyleConstants.STYLE_PADDING_RIGHT).getCssText()
: ("0" + STYLE_OVERLAY_DEFAULT_UNIT);
// Cell padding values
double paddingLeftValueCellContent = smu.convertDimensionToMillimetres(DimensionType
.parserUnit(paddingLeftCellContent));
double paddingRightValueCellContent = smu.convertDimensionToMillimetres(DimensionType
.parserUnit(paddingRightCellContent));
// Validation of the text indent mode
if (birtCellStyle.getTextIndentMode().equals(ExcelEmitter.TEXT_INDENT_MODE_SPACING_CELL)) {
// use cell padding to calculate the indent (reset element spacing)
marginLeftElementContent = new StringValue(CSSPrimitiveValue.CSS_STRING,
"0" + STYLE_OVERLAY_DEFAULT_UNIT);
marginRightElementContent = new StringValue(CSSPrimitiveValue.CSS_STRING,
"0" + STYLE_OVERLAY_DEFAULT_UNIT);
paddingLeftValueElementContent = 0.0;
paddingRightValueElementContent = 0.0;
} else if (birtCellStyle.getTextIndentMode().equals(ExcelEmitter.TEXT_INDENT_MODE_SPACING_ELEMENT)) {
// use element padding & margin to calculate the indent (reset cell spacing)
paddingLeftValueCellContent = 0.0;
paddingRightValueCellContent = 0.0;
} else {
// use cell padding & element padding & element margin to calculate the indent
}
// Sum of padding values rounded based on 4 decimals
double paddingLeftValueNew = Math
.round((paddingLeftValueElementContent + paddingLeftValueCellContent) * 1000.0) / 1000.0;
double paddingRightValueNew = Math
.round((paddingRightValueElementContent + paddingRightValueCellContent) * 1000.0) / 1000.0;
// Set the margin and padding for the finalized cell style
birtCellStyle.setProperty(StyleConstants.STYLE_MARGIN_LEFT, marginLeftElementContent);
birtCellStyle.setProperty(StyleConstants.STYLE_MARGIN_RIGHT, marginRightElementContent);
birtCellStyle.setProperty(StyleConstants.STYLE_PADDING_LEFT,
new StringValue(CSSPrimitiveValue.CSS_STRING, paddingLeftValueNew + STYLE_OVERLAY_DEFAULT_UNIT));
birtCellStyle.setProperty(StyleConstants.STYLE_PADDING_RIGHT,
new StringValue(CSSPrimitiveValue.CSS_STRING, paddingRightValueNew + STYLE_OVERLAY_DEFAULT_UNIT));
// Override based on the original color content values
if (elemStyle.getProperty(StyleConstants.STYLE_BACKGROUND_COLOR) != null) {
birtCellStyle.setProperty(StyleConstants.STYLE_BACKGROUND_COLOR,
elemStyle.getProperty(StyleConstants.STYLE_BACKGROUND_COLOR));
}
if (elemStyle.getProperty(StyleConstants.STYLE_COLOR) != null) {
birtCellStyle.setProperty(StyleConstants.STYLE_COLOR,
elemStyle.getProperty(StyleConstants.STYLE_COLOR));
}
}
return birtCellStyle;
}
/**
* Calculate the width of a set of columns, in millimetres.
*
* @param startCol The first column to consider (inclusive).
* @param endCol The last column to consider (inclusive).
* @return The sum of the widths of all columns between startCol and endCol
* (inclusive) in millimetres.
*/
private double spanWidthMillimetres(Sheet sheet, int startCol, int endCol) {
int result = 0;
for (int columnIndex = startCol; columnIndex <= endCol; ++columnIndex) {
result += sheet.getColumnWidth(columnIndex);
}
return ClientAnchorConversions.widthUnits2Millimetres(result);
}
/**
* Set the contents of an empty cell. This should now be the only way in which a
* cell value is set (cells should not be modified).
*
* @param value The value to set.
* @param element The BIRT element supplying the value, used to set the style of
* the cell.
*/
private void setCellContents(Cell cell, Object value, String formula) {
log.debug("Setting cell[", cell.getRow().getRowNum(), ",", cell.getColumnIndex(), "] value to ", value);
if (formula != null) {
formula = formula.trim();
if (formula.startsWith("=")) {
formula = formula.substring(1).trim();
cell.setCellFormula(formula);
} else {
value = formula;
formula = null;
}
}
if (value != null) {
if (value instanceof Double) {
// cell.setCellType(Cell.CELL_TYPE_NUMERIC);
cell.setCellValue((Double) value);
lastValue = value;
} else if (value instanceof Integer) {
// cell.setCellType(Cell.CELL_TYPE_NUMERIC);
cell.setCellValue((Integer) value);
lastValue = value;
} else if (value instanceof Long) {
// cell.setCellType(Cell.CELL_TYPE_NUMERIC);
cell.setCellValue((Long) value);
lastValue = value;
} else if (value instanceof Date) {
// cell.setCellType(Cell.CELL_TYPE_NUMERIC);
cell.setCellValue((Date) value);
lastValue = value;
} else if (value instanceof Boolean) {
// cell.setCellType(Cell.CELL_TYPE_BOOLEAN);
cell.setCellValue(((Boolean) value).booleanValue());
lastValue = value;
} else if (value instanceof BigDecimal) {
// cell.setCellType(Cell.CELL_TYPE_NUMERIC);
cell.setCellValue(((BigDecimal) value).doubleValue());
lastValue = value;
} else if (value instanceof String) {
// cell.setCellType(Cell.CELL_TYPE_STRING);
cell.setCellValue((String) value);
lastValue = value;
} else if (value instanceof RichTextString) {
// cell.setCellType(Cell.CELL_TYPE_STRING);
cell.setCellValue((RichTextString) value);
lastValue = value;
} else if (value != null) {
log.debug("Unhandled data: ", (value == null ? "" : value));
// cell.setCellType(Cell.CELL_TYPE_STRING);
cell.setCellValue(value.toString());
lastValue = value;
}
}
}
/**
* Set the style of the current cell based on the style of a BIRT element.
*
* @param element The BIRT element to take the style from.
*/
@SuppressWarnings("deprecation")
private void setCellStyle(StyleManager sm, Cell cell, BirtStyle birtStyle, Object value) {
if ((StyleManagerUtils.getNumberFormat(birtStyle) == null)
&& (StyleManagerUtils.getDateFormat(birtStyle) == null)
&& (StyleManagerUtils.getDateTimeFormat(birtStyle) == null)
&& (StyleManagerUtils.getTimeFormat(birtStyle) == null) && (value != null)) {
if (value instanceof Date) {
long time = ((Date) value).getTime();
time = time - ((Date) value).getTimezoneOffset() * 60000;
if (time % oneDay == 0) {
StyleManagerUtils.setDateFormat(birtStyle, "Short Date", null);
} else if (time < oneDay) {
StyleManagerUtils.setTimeFormat(birtStyle, "Short Time", null);
} else {
StyleManagerUtils.setDateTimeFormat(birtStyle, "General Date", null);
}
}
}
// log.debug( "BirtStyle: ", birtStyle);
CellStyle cellStyle = sm.getStyle(birtStyle);
cell.setCellStyle(cellStyle);
}
private CSSValue preferredAlignment(BirtStyle elementStyle) {
CSSValue newAlign = elementStyle.getProperty(StyleConstants.STYLE_TEXT_ALIGN);
if (newAlign == null) {
newAlign = new StringValue(CSSPrimitiveValue.CSS_STRING, CSSConstants.CSS_LEFT_VALUE);
}
if (preferredAlignment == null) {
return newAlign;
}
if (CSSConstants.CSS_LEFT_VALUE.equals(newAlign.getCssText())) {
return newAlign;
} else if (CSSConstants.CSS_RIGHT_VALUE.equals(newAlign.getCssText())) {
if (CSSConstants.CSS_CENTER_VALUE.equals(preferredAlignment.getCssText())) {
return newAlign;
}
return preferredAlignment;
}
return preferredAlignment;
}
/**
* Set the contents of the current cell. If the current cell is empty this will
* format the cell optimally for the new value, if the current cell already has
* some contents this will simply append the text value to the current contents.
*
* @param value The value to put into the current cell.
*/
protected void emitContent(HandlerState state, IContent element, Object value, boolean asBlock) {
if (value == null) {
return;
}
if (element.getBookmark() != null) {
createName(state, prepareName(element.getBookmark()), state.rowNum, state.colNum, state.rowNum,
state.colNum);
}
String formula = EmitterServices.stringOption(null, element, ExcelEmitter.FORMULA, null);
if ((formula == null) && EmitterServices.booleanOption(null, element, ExcelEmitter.VALUE_AS_FORMULA, false)) {
formula = value != null ? value.toString() : null;
value = null;
}
if (formula != null) {
lastFormula = formula;
}
if (value == null && formula == null) {
return;
}
if ((lastValue == null) && (value != null || formula != null)) {
lastValue = (value != null) ? value : formula;
lastElement = element;
lastCellContentsWasBlock = asBlock;
IHyperlinkAction birtHyperlink = element.getHyperlinkAction();
if (birtHyperlink != null) {
switch (birtHyperlink.getType()) {
case IHyperlinkAction.ACTION_HYPERLINK:
hyperlinkUrl = birtHyperlink.getHyperlink();
break;
case IHyperlinkAction.ACTION_BOOKMARK:
hyperlinkBookmark = birtHyperlink.getBookmark();
break;
case IHyperlinkAction.ACTION_DRILLTHROUGH:
IHTMLActionHandler handler = state.getRenderOptions().getActionHandler();
if (handler != null) {
hyperlinkUrl = handler.getURL(new Action(null, birtHyperlink),
element.getReportContent().getReportContext());
}
break;
default:
log.debug("Unhandled hyperlink type: {}", birtHyperlink.getType());
}
}
return;
}
StyleManager sm = state.getSm();
if (lastValue != null) {
// Both to be improved to include formatting
String oldValue = lastValue.toString();
String newComponent = value.toString();
if (lastCellContentsWasBlock && !newComponent.startsWith("\n") && !oldValue.endsWith("\n")) {
oldValue = oldValue + "\n";
lastCellContentsWasBlock = false;
}
if (lastCellContentsRequiresSpace && !newComponent.startsWith("\n") && !oldValue.endsWith("\n")) {
oldValue = oldValue + " ";
lastCellContentsRequiresSpace = false;
}
String newValue = oldValue + newComponent;
lastValue = newValue;
if (element != null) {
BirtStyle elementStyle = new BirtStyle(element);
Font newFont = sm.getFontManager().getFont(elementStyle);
richTextRuns.add(new RichTextRun(oldValue.length(), newFont));
preferredAlignment = preferredAlignment(elementStyle);
}
}
lastCellContentsWasBlock = asBlock;
hyperlinkUrl = null;
}
/**
* Record image
*
* @param state handler state
* @param location location coordinate
* @param image image content
* @param spanColumns columns are spanned
* @throws BirtException
*/
public void recordImage(HandlerState state, Coordinate location, IImageContent image, boolean spanColumns)
throws BirtException {
byte[] data = image.getData();
log.debug("startImage: " + "[" + image.getMIMEType() + "] " + "{" + image.getWidth() + " x " + image.getHeight()
+ "} " + (data == null ? "(no data) " : "(" + data.length + " bytes) ") + image.getURI());
StyleManagerUtils smu = state.getSmu();
Workbook wb = state.getWb();
String mimeType = image.getMIMEType();
String stringURI = image.getURI();
if (stringURI != null
&& (stringURI.toLowerCase().endsWith(".svg") || stringURI.toLowerCase().contains(URL_IMAGE_TYPE_SVG))
|| mimeType != null && mimeType.toLowerCase().equals(URL_IMAGE_TYPE_SVG)) {
try {
String encodedImg = null;
String decodedImg = null;
if (stringURI != null && stringURI.toLowerCase().contains(URL_IMAGE_TYPE_SVG)) {
// svg: url stream image
String svgSplitter = "svg\\+xml,";
if (stringURI.contains(URL_IMAGE_TYPE_SVG + URL_PROTOCOL_TYPE_DATA_UTF8)) {
svgSplitter = "svg\\+xml;utf8,";
} else if (stringURI.contains(URL_IMAGE_TYPE_SVG + URL_PROTOCOL_TYPE_DATA_BASE)) {
svgSplitter = "svg\\+xml;base64,";
}
String[] uriParts = stringURI.split(svgSplitter);
if (uriParts.length >= 2) {
encodedImg = uriParts[1];
decodedImg = encodedImg;
if (stringURI.contains(URL_PROTOCOL_TYPE_DATA_BASE)) {
decodedImg = new String(
Base64.getDecoder().decode(encodedImg.getBytes(StandardCharsets.UTF_8)));
}
}
} else {
// svg: url file load
if (data == null && stringURI != null) {
String uri = this.verifyURI(stringURI); // URL(this.id);
URL imageUrl = new URL(uri);
URLConnection conn = imageUrl.openConnection();
conn.connect();
data = smu.downloadImage(conn);
image.setData(data);
}
decodedImg = new String(image.getData());
}
try {
decodedImg = java.net.URLDecoder.decode(decodedImg, StandardCharsets.UTF_8);
} catch (IllegalArgumentException iae) {
// do nothing
}
data = SvgFile.transSvgToArray(new ByteArrayInputStream(decodedImg.getBytes()));
} catch (Exception e) {
// invalid svg image, default handling
}
} else if (stringURI != null && stringURI.startsWith(URL_PROTOCOL_TYPE_DATA)
&& stringURI.contains(URL_PROTOCOL_TYPE_DATA_BASE)) {
String base64[] = image.getURI().toString().split(URL_PROTOCOL_TYPE_DATA_BASE);
if (base64.length >= 2) {
data = Base64.getDecoder().decode(base64[1]);
}
} else if ((data == null) && (image.getURI() != null)) {
try {
URL imageUrl = new URL(this.verifyURI(image.getURI()));
URLConnection conn = imageUrl.openConnection();
conn.connect();
mimeType = conn.getContentType();
int imageType = smu.poiImageTypeFromMimeType(mimeType, null);
if (imageType == 0) {
log.debug("Unrecognised/unhandled image MIME type: " + mimeType);
} else {
data = smu.downloadImage(conn);
image.setData(data);
}
} catch (IOException ex) {
log.debug(ex.getClass(), ": ", ex.getMessage());
ex.printStackTrace();
}
}
if (data != null) {
int imageType = smu.poiImageTypeFromMimeType(mimeType, data);
if (imageType == 0) {
log.debug("Unrecognised/unhandled image MIME type: ", image.getMIMEType());
} else {
int imageIdx = wb.addPicture(data, imageType);
if ((image.getHeight() == null) || (image.getWidth() == null)) {
Image birtImage = new Image();
birtImage.setInput(data);
birtImage.check();
log.debug("Calculated image dimensions " + birtImage.getWidth() + " (@"
+ birtImage.getPhysicalWidthDpi() + "dpi=" + birtImage.getPhysicalWidthInch() + "in) x "
+ birtImage.getHeight() + " (@" + birtImage.getPhysicalHeightDpi() + "dpi="
+ birtImage.getPhysicalHeightInch() + "in)");
if (image.getWidth() == null) {
DimensionType Width = new DimensionType(
(birtImage.getPhysicalWidthInch() > 0) ? birtImage.getPhysicalWidthInch()
: birtImage.getWidth() / 96.0,
"in");
image.setWidth(Width);
}
if (image.getHeight() == null) {
DimensionType Height = new DimensionType(
(birtImage.getPhysicalHeightInch() > 0) ? birtImage.getPhysicalHeightInch()
: birtImage.getHeight() / 96.0,
"in");
image.setHeight(Height);
}
}
state.images.add(new CellImage(location, imageIdx, image, spanColumns));
lastElement = image;
}
}
}
protected void removeMergedCell(HandlerState state, int row, int col) {
for (int mergeNum = 0; mergeNum < state.currentSheet.getNumMergedRegions(); ++mergeNum) {
CellRangeAddress region = state.currentSheet.getMergedRegion(mergeNum);
if ((region.getFirstRow() == row) && (region.getFirstColumn() == col)) {
state.currentSheet.removeMergedRegion(mergeNum);
break;
}
}
for (Iterator iter = state.rowSpans.iterator(); iter.hasNext();) {
Area area = iter.next();
Coordinate topLeft = area.getX();
if ((topLeft.getRow() == row) || (topLeft.getCol() == col)) {
iter.remove();
}
}
}
/**
* Check the URL to be valid and fall back try it like file-URL
*/
private String verifyURI(String uri) {
if (uri != null && !uri.toLowerCase().startsWith(URL_PROTOCOL_TYPE_DATA)) {
String tmpUrl = uri.replaceAll(" ", URL_PROTOCOL_URL_ENCODED_SPACE);
try {
new URL(tmpUrl).toURI();
} catch (MalformedURLException | URISyntaxException excUrl) {
// invalid URI try it like "file:///"
try {
tmpUrl = URL_PROTOCOL_TYPE_FILE + "///" + uri;
new URL(tmpUrl).toURI();
uri = tmpUrl;
} catch (MalformedURLException | URISyntaxException excFile) {
}
}
}
return uri;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy