net.sf.jasperreports.engine.export.JRXlsMetadataExporter.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.jasperreports.engine.export.JRXlsMetadataExporter.java

Source

/*
 * JasperReports - Free Java Reporting Library.
 * Copyright (C) 2001 - 2019 TIBCO Software Inc. All rights reserved.
 * http://www.jaspersoft.com
 *
 * Unless you have purchased a commercial license agreement from Jaspersoft,
 * the following license terms apply:
 *
 * This program is part of JasperReports.
 *
 * JasperReports is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JasperReports 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.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with JasperReports. If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * Contributor: Manuel Paul <mpaul@ratundtat.com>,
 *            Rat & Tat Beratungsgesellschaft mbH,
 *            Muehlenkamp 6c,
 *            22303 Hamburg,
 *            Germany.
 */
package net.sf.jasperreports.engine.export;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.font.TextAttribute;
import java.awt.geom.Dimension2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.AttributedCharacterIterator;
import java.text.AttributedCharacterIterator.Attribute;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.apache.commons.collections4.map.ReferenceMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.poi.common.usermodel.HyperlinkType;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
import org.apache.poi.hssf.usermodel.HSSFDataFormat;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFFooter;
import org.apache.poi.hssf.usermodel.HSSFHeader;
import org.apache.poi.hssf.usermodel.HSSFName;
import org.apache.poi.hssf.usermodel.HSSFPalette;
import org.apache.poi.hssf.usermodel.HSSFPatriarch;
import org.apache.poi.hssf.usermodel.HSSFPrintSetup;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.usermodel.HeaderFooter;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
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.VerticalAlignment;
import org.apache.poi.ss.util.CellReference;

import net.sf.jasperreports.engine.DefaultJasperReportsContext;
import net.sf.jasperreports.engine.JRBoxContainer;
import net.sf.jasperreports.engine.JRCommonGraphicElement;
import net.sf.jasperreports.engine.JRCommonText;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRFont;
import net.sf.jasperreports.engine.JRGenericElementType;
import net.sf.jasperreports.engine.JRGenericPrintElement;
import net.sf.jasperreports.engine.JRLineBox;
import net.sf.jasperreports.engine.JRPen;
import net.sf.jasperreports.engine.JRPrintElement;
import net.sf.jasperreports.engine.JRPrintEllipse;
import net.sf.jasperreports.engine.JRPrintFrame;
import net.sf.jasperreports.engine.JRPrintGraphicElement;
import net.sf.jasperreports.engine.JRPrintHyperlink;
import net.sf.jasperreports.engine.JRPrintImage;
import net.sf.jasperreports.engine.JRPrintLine;
import net.sf.jasperreports.engine.JRPrintRectangle;
import net.sf.jasperreports.engine.JRPrintText;
import net.sf.jasperreports.engine.JRPropertiesUtil;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JasperReportsContext;
import net.sf.jasperreports.engine.base.JRBaseFont;
import net.sf.jasperreports.engine.export.data.BooleanTextValue;
import net.sf.jasperreports.engine.export.data.DateTextValue;
import net.sf.jasperreports.engine.export.data.NumberTextValue;
import net.sf.jasperreports.engine.export.data.StringTextValue;
import net.sf.jasperreports.engine.export.data.TextValue;
import net.sf.jasperreports.engine.export.data.TextValueHandler;
import net.sf.jasperreports.engine.export.type.ImageAnchorTypeEnum;
import net.sf.jasperreports.engine.type.ImageTypeEnum;
import net.sf.jasperreports.engine.type.LineDirectionEnum;
import net.sf.jasperreports.engine.type.ModeEnum;
import net.sf.jasperreports.engine.type.OrientationEnum;
import net.sf.jasperreports.engine.type.RunDirectionEnum;
import net.sf.jasperreports.engine.util.DefaultFormatFactory;
import net.sf.jasperreports.engine.util.ImageUtil;
import net.sf.jasperreports.engine.util.JRDataUtils;
import net.sf.jasperreports.engine.util.JRImageLoader;
import net.sf.jasperreports.engine.util.JRStringUtil;
import net.sf.jasperreports.engine.util.JRStyledText;
import net.sf.jasperreports.export.XlsExporterConfiguration;
import net.sf.jasperreports.export.XlsMetadataExporterConfiguration;
import net.sf.jasperreports.export.XlsMetadataReportConfiguration;
import net.sf.jasperreports.export.XlsReportConfiguration;
import net.sf.jasperreports.renderers.DataRenderable;
import net.sf.jasperreports.renderers.DimensionRenderable;
import net.sf.jasperreports.renderers.Graphics2DRenderable;
import net.sf.jasperreports.renderers.Renderable;
import net.sf.jasperreports.renderers.RenderersCache;
import net.sf.jasperreports.renderers.ResourceRenderer;

/**
 * @author Sanda Zaharia (shertage@users.sourceforge.net)
 */
public class JRXlsMetadataExporter extends
        JRXlsAbstractMetadataExporter<XlsMetadataReportConfiguration, XlsMetadataExporterConfiguration, JRXlsExporterContext> {

    private static final Log log = LogFactory.getLog(JRXlsMetadataExporter.class);

    /**
     * The exporter key, as used in
     * {@link GenericElementHandlerEnviroment#getElementHandler(JRGenericElementType, String)}.
     */
    public static final String XLS_EXPORTER_KEY = JRPropertiesUtil.PROPERTY_PREFIX + "xls";
    public static short MAX_COLOR_INDEX = 56;
    public static short MIN_COLOR_INDEX = 10; /* Indexes from 0 to 9 are reserved */
    public static String CURRENT_ROW_HEIGHT = "CURRENT_ROW_HEIGHT";

    private static Map<Color, HSSFColor> hssfColorsCache = new ReferenceMap<Color, HSSFColor>();

    protected final DateFormat isoDateFormat = JRDataUtils.getIsoDateFormat();

    protected Map<StyleInfo, HSSFCellStyle> loadedCellStyles = new HashMap<StyleInfo, HSSFCellStyle>();
    protected Map<String, List<Hyperlink>> anchorLinks = new HashMap<String, List<Hyperlink>>();
    protected Map<Integer, List<Hyperlink>> pageLinks = new HashMap<Integer, List<Hyperlink>>();
    protected Map<String, HSSFName> anchorNames = new HashMap<String, HSSFName>();

    protected HSSFWorkbook workbook;
    protected HSSFSheet sheet;
    protected HSSFRow row;
    protected HSSFCell cell;
    protected HSSFCellStyle emptyCellStyle;
    protected CreationHelper createHelper;
    private HSSFPalette palette = null;
    protected Map<String, HSSFCellStyle> columnStylesMap;
    protected Map<String, Integer> columnWidths;
    protected Map<String, Float> columnWidthRatios;
    protected Map<HSSFCell, String> formulaCellsMap;

    /**
     *
     */
    protected short whiteIndex = HSSFColor.HSSFColorPredefined.WHITE.getIndex();
    protected short blackIndex = HSSFColor.HSSFColorPredefined.BLACK.getIndex();
    protected short customColorIndex = MIN_COLOR_INDEX;

    protected FillPatternType backgroundMode = FillPatternType.SOLID_FOREGROUND;

    protected HSSFDataFormat dataFormat;

    protected HSSFPatriarch patriarch;

    protected static final String EMPTY_SHEET_NAME = "Sheet1";

    protected class ExporterContext extends BaseExporterContext implements JRXlsExporterContext {
    }

    /**
     * @see #JRXlsMetadataExporter(JasperReportsContext)
     */
    public JRXlsMetadataExporter() {
        this(DefaultJasperReportsContext.getInstance());
    }

    /**
     *
     */
    public JRXlsMetadataExporter(JasperReportsContext jasperReportsContext) {
        super(jasperReportsContext);

        exporterContext = new ExporterContext();
    }

    @Override
    protected Class<XlsMetadataExporterConfiguration> getConfigurationInterface() {
        return XlsMetadataExporterConfiguration.class;
    }

    @Override
    protected Class<XlsMetadataReportConfiguration> getItemConfigurationInterface() {
        return XlsMetadataReportConfiguration.class;
    }

    @Override
    protected void initExport() {
        super.initExport();

        sheet = null;
    }

    @Override
    protected void initReport() {
        super.initReport();

        XlsReportConfiguration configuration = getCurrentItemConfiguration();

        if (!configuration.isWhitePageBackground()) {
            backgroundMode = FillPatternType.NO_FILL;
        }

        nature = new JRXlsMetadataExporterNature(jasperReportsContext, filter, configuration.isIgnoreGraphics(),
                configuration.isIgnorePageMargins());
    }

    @Override
    protected void openWorkbook(OutputStream os) throws JRException {
        XlsMetadataExporterConfiguration configuration = getCurrentConfiguration();
        String lcWorkbookTemplate = workbookTemplate == null ? configuration.getWorkbookTemplate()
                : workbookTemplate;
        if (lcWorkbookTemplate == null) {
            workbook = new HSSFWorkbook();
        } else {
            InputStream templateIs = null;
            try {
                templateIs = getRepository().getInputStreamFromLocation(lcWorkbookTemplate);
                if (templateIs == null) {
                    throw new JRRuntimeException(EXCEPTION_MESSAGE_KEY_TEMPLATE_NOT_FOUND,
                            new Object[] { lcWorkbookTemplate });
                } else {
                    workbook = new HSSFWorkbook(new POIFSFileSystem(templateIs));
                    boolean keepSheets = keepTemplateSheets == null ? configuration.isKeepWorkbookTemplateSheets()
                            : keepTemplateSheets;
                    if (keepSheets) {
                        sheetIndex += workbook.getNumberOfSheets();
                    } else {
                        for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
                            workbook.removeSheetAt(i);
                        }
                    }
                }
            } catch (JRException e) {
                throw new JRRuntimeException(e);
            } catch (IOException e) {
                throw new JRRuntimeException(e);
            } finally {
                if (templateIs != null) {
                    try {
                        templateIs.close();
                    } catch (IOException e) {
                    }
                }
            }
        }
        emptyCellStyle = workbook.createCellStyle();
        emptyCellStyle.setFillForegroundColor(HSSFColor.HSSFColorPredefined.WHITE.getIndex());
        emptyCellStyle.setFillPattern(backgroundMode);
        dataFormat = workbook.createDataFormat();
        createHelper = workbook.getCreationHelper();
        firstPageNotSet = true;
        palette = workbook.getCustomPalette();
        customColorIndex = MIN_COLOR_INDEX;
        columnWidths = new HashMap<String, Integer>();
        columnWidthRatios = new HashMap<String, Float>();
        formulaCellsMap = new HashMap<HSSFCell, String>();
    }

    @Override
    protected void createSheet(SheetInfo sheetInfo) {
        this.sheetInfo = sheetInfo;
        sheet = workbook.createSheet(sheetInfo.sheetName);
        patriarch = sheet.createDrawingPatriarch();
        HSSFPrintSetup printSetup = sheet.getPrintSetup();
        printSetup.setLandscape(pageFormat.getOrientation() == OrientationEnum.LANDSCAPE);
        short paperSize = getSuitablePaperSize();

        XlsReportConfiguration configuration = getCurrentItemConfiguration();

        if (paperSize != -1) {
            printSetup.setPaperSize(paperSize);
        }

        String password = configuration.getPassword();
        if (password != null) {
            sheet.protectSheet(password);
        }

        sheet.setMargin(Sheet.LeftMargin, LengthUtil.inch(configuration.getPrintPageLeftMargin()));
        sheet.setMargin(Sheet.RightMargin, LengthUtil.inch(configuration.getPrintPageRightMargin()));
        sheet.setMargin(Sheet.TopMargin, LengthUtil.inch(configuration.getPrintPageTopMargin()));
        sheet.setMargin(Sheet.BottomMargin, LengthUtil.inch(configuration.getPrintPageBottomMargin()));

        if (configuration.getSheetHeaderLeft() != null) {
            sheet.getHeader().setLeft(configuration.getSheetHeaderLeft());
        }

        if (configuration.getSheetHeaderCenter() != null) {
            sheet.getHeader().setCenter(configuration.getSheetHeaderCenter());
        }

        if (configuration.getSheetHeaderRight() != null) {
            sheet.getHeader().setRight(configuration.getSheetHeaderRight());
        }

        if (configuration.getSheetFooterLeft() != null) {
            sheet.getFooter().setLeft(configuration.getSheetFooterLeft());
        }

        if (configuration.getSheetFooterCenter() != null) {
            sheet.getFooter().setCenter(configuration.getSheetFooterCenter());
        }

        if (configuration.getSheetFooterRight() != null) {
            sheet.getFooter().setRight(configuration.getSheetFooterRight());
        }

        printSetup.setHeaderMargin(LengthUtil.inch(configuration.getPrintHeaderMargin()));
        printSetup.setFooterMargin(LengthUtil.inch(configuration.getPrintFooterMargin()));

        RunDirectionEnum sheetDirection = configuration.getSheetDirection();
        if (sheetDirection != null) {
            printSetup.setLeftToRight(sheetDirection == RunDirectionEnum.LTR);
            sheet.setRightToLeft(sheetDirection == RunDirectionEnum.RTL);
        }

        if (sheetInfo.sheetFirstPageNumber != null && sheetInfo.sheetFirstPageNumber > 0) {
            printSetup.setPageStart((short) sheetInfo.sheetFirstPageNumber.intValue());
            printSetup.setUsePage(true);
            firstPageNotSet = false;
        } else {
            Integer documentFirstPageNumber = configuration.getFirstPageNumber();
            if (documentFirstPageNumber != null && documentFirstPageNumber > 0 && firstPageNotSet) {
                printSetup.setPageStart((short) documentFirstPageNumber.intValue());
                printSetup.setUsePage(true);
                firstPageNotSet = false;
            }
        }
        if (!firstPageNotSet
                && (sheet.getFooter().getCenter() == null || sheet.getFooter().getCenter().length() == 0)) {
            sheet.getFooter().setCenter("Page " + HeaderFooter.page());
        }

        boolean showGridlines = true;
        if (sheetInfo.sheetShowGridlines == null) {
            Boolean documentShowGridlines = configuration.isShowGridLines();
            if (documentShowGridlines != null) {
                showGridlines = documentShowGridlines;
            }
        } else {
            showGridlines = sheetInfo.sheetShowGridlines;
        }
        sheet.setDisplayGridlines(showGridlines);

        backgroundMode = Boolean.TRUE.equals(sheetInfo.whitePageBackground) ? FillPatternType.SOLID_FOREGROUND
                : FillPatternType.NO_FILL;

        //      maxRowFreezeIndex = 0;
        //      maxColumnFreezeIndex = 0;

        onePagePerSheetMap.put(sheetIndex, configuration.isOnePagePerSheet());
        sheetsBeforeCurrentReportMap.put(sheetIndex, sheetsBeforeCurrentReport);
    }

    @Override
    protected void closeSheet() {
        if (sheet == null) {
            return;
        }

        HSSFPrintSetup printSetup = sheet.getPrintSetup();

        if (isValidScale(sheetInfo.sheetPageScale)) {
            printSetup.setScale((short) sheetInfo.sheetPageScale.intValue());
        } else {
            XlsReportConfiguration configuration = getCurrentItemConfiguration();

            Integer fitWidth = configuration.getFitWidth();
            if (fitWidth != null) {
                printSetup.setFitWidth(fitWidth.shortValue());
                sheet.setAutobreaks(true);
            }

            Integer fitHeight = configuration.getFitHeight();
            fitHeight = fitHeight == null ? (Boolean.TRUE == configuration.isAutoFitPageHeight()
                    ? (pageIndex - sheetInfo.sheetFirstPageIndex)
                    : null) : fitHeight;
            if (fitHeight != null) {
                printSetup.setFitHeight(fitHeight.shortValue());
                sheet.setAutobreaks(true);
            }
        }
    }

    @Override
    protected void closeWorkbook(OutputStream os) throws JRException {
        try {
            for (Object anchorName : anchorNames.keySet()) {
                HSSFName anchor = anchorNames.get(anchorName);
                List<Hyperlink> linkList = anchorLinks.get(anchorName);
                anchor.setRefersToFormula(
                        "'" + workbook.getSheetName(anchor.getSheetIndex()) + "'!" + anchor.getRefersToFormula());

                if (linkList != null && !linkList.isEmpty()) {
                    for (Hyperlink link : linkList) {
                        link.setAddress(anchor.getRefersToFormula());
                    }
                }
            }

            if (!definedNamesMap.isEmpty()) {
                for (Map.Entry<NameScope, String> entry : definedNamesMap.entrySet()) {
                    HSSFName name = workbook.createName();
                    NameScope nameScope = entry.getKey();
                    name.setNameName(nameScope.getName());
                    name.setRefersToFormula(entry.getValue());
                    int scopeIndex = workbook.getSheetIndex(nameScope.getScope());
                    // name and name scope are ignoring case in Excel
                    if (nameScope.getScope() != null
                            && !DEFAULT_DEFINED_NAME_SCOPE.equalsIgnoreCase(nameScope.getScope())
                            && scopeIndex >= 0) {
                        name.setSheetIndex(scopeIndex);
                    }
                }
            }

            // applying formulas
            if (formulaCellsMap != null && !formulaCellsMap.isEmpty()) {
                for (Map.Entry<HSSFCell, String> formulaCell : formulaCellsMap.entrySet()) {
                    try {
                        formulaCell.getKey().setCellFormula(formulaCell.getValue());
                    } catch (Exception e) {
                        // usually an org.apache.poi.ss.formula.FormulaParseException 
                        // or a java.lang.IllegalArgumentException
                        // or a java.lang.IllegalStateException
                        if (log.isWarnEnabled()) {
                            log.warn(e.getMessage());
                        }
                        throw new JRException(e);
                    }
                }
            }

            int index = 0;
            for (Integer linkPage : pageLinks.keySet()) {
                List<Hyperlink> linkList = pageLinks.get(linkPage);
                if (linkList != null && !linkList.isEmpty()) {
                    for (Hyperlink link : linkList) {
                        index = onePagePerSheetMap.get(linkPage - 1) != null
                                ? (onePagePerSheetMap.get(linkPage - 1) ? Math.max(0, linkPage - 1)
                                        : Math.max(0, sheetsBeforeCurrentReportMap.get(linkPage)))
                                : 0;
                        link.setAddress("'" + workbook.getSheetName(index) + "'!$A$1");
                    }
                }
            }

            for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
                HSSFSheet currentSheet = workbook.getSheetAt(i);
                currentSheet.setForceFormulaRecalculation(true);
                for (String columnName : columnNames) {
                    Integer columnWidth = columnWidths.get(columnName);
                    Float columnWidthRatio = columnWidthRatios.get(columnName);
                    if (columnWidth != null && columnWidth < Integer.MAX_VALUE) {
                        if (columnWidthRatio != null && columnWidthRatio > 1f) {
                            columnWidth = Math.round(43 * columnWidth * columnWidthRatio);
                        } else {
                            columnWidth = 43 * columnWidth;
                        }
                        currentSheet.setColumnWidth(columnNamesMap.get(columnName),
                                Math.min(columnWidth, 256 * 255));
                    } else {
                        currentSheet.autoSizeColumn(columnNamesMap.get(columnName), false);
                    }
                }
            }
            workbook.write(os);
        } catch (IOException e) {
            throw new JRException(EXCEPTION_MESSAGE_KEY_REPORT_GENERATION_ERROR,
                    new Object[] { jasperPrint.getName() }, e);
        }
    }

    @Override
    protected void setColumnWidth(int col, int width) {
    }

    protected void setRowHeight(HSSFRow row) {
        Integer rowHeight = (Integer) currentRow.get(CURRENT_ROW_HEIGHT);
        if (row != null && rowHeight != null && rowHeight < Integer.MAX_VALUE) {
            row.setHeightInPoints((Integer) currentRow.get(CURRENT_ROW_HEIGHT));
        }
    }

    protected void adjustRowHeight(int rowHeight, Boolean isAutofit) {
        if (isAutofit != null && isAutofit) {
            currentRow.put(CURRENT_ROW_HEIGHT, Integer.MAX_VALUE);
        } else if (!currentRow.containsKey(CURRENT_ROW_HEIGHT)
                || (Integer) currentRow.get(CURRENT_ROW_HEIGHT) < rowHeight) {
            currentRow.put(CURRENT_ROW_HEIGHT, rowHeight);
        }
    }

    protected void adjustColumnWidth(String columnName, int columnWidth, Boolean isAutofit) {
        if (isAutofit != null && isAutofit) {
            columnWidths.put(columnName, Integer.MAX_VALUE);
        } else {
            if (!columnWidths.containsKey(columnName) || columnWidths.get(columnName) < columnWidth) {
                columnWidths.put(columnName, columnWidth);
            }
            if (!columnWidthRatios.containsKey(columnName) && sheetInfo.columnWidthRatio != null) {
                columnWidthRatios.put(columnName, sheetInfo.columnWidthRatio);
            }
        }
    }

    protected void addBlankCell(HSSFCellStyle cellStyle, Map<String, Object> cellValueMap, String currentColumnName)
            throws JRException {
        HSSFCellStyle currentStyle = cellStyle != null ? cellStyle
                : (columnStylesMap.get(currentColumnName) != null ? columnStylesMap.get(currentColumnName)
                        : emptyCellStyle);
        cellValueMap.put(currentColumnName, new CellSettings(currentStyle));
    }

    @Override
    protected void writeCurrentRow(Map<String, Object> currentRow, Map<String, Object> repeatedValues)
            throws JRException {
        row = sheet.createRow(sheet.getPhysicalNumberOfRows());
        setRowHeight(row);

        for (int i = 0; i < columnNames.size(); i++) {
            String columnName = columnNames.get(i);
            CellSettings cellSettings = (CellSettings) currentRow.get(columnName) == null
                    ? (repeatedValues.get(columnName) != null ? (CellSettings) repeatedValues.get(columnName)
                            : null)
                    : (CellSettings) currentRow.get(columnName);
            cell = row.createCell(i);
            if (cellSettings != null) {
                CellType type = cellSettings.getCellType();
                cell.setCellType(type);
                Object cellValue = cellSettings.getCellValue();
                if (cellValue != null) {
                    if (cellValue instanceof RichTextString) {
                        cell.setCellValue((RichTextString) cellSettings.getCellValue());
                    } else if (cellValue instanceof Number) {
                        cell.setCellValue(((Number) cellSettings.getCellValue()).doubleValue());
                    } else if (cellValue instanceof Date) {
                        cell.setCellValue((Date) cellSettings.getCellValue());
                    } else if (cellValue instanceof Boolean) {
                        cell.setCellValue((Boolean) cellSettings.getCellValue());
                    } else if (cellValue instanceof ImageSettings) {
                        ImageSettings imageSettings = (ImageSettings) cellValue;
                        try {
                            HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0, 0, 0, (short) (i), rowIndex,
                                    (short) (i + 1), rowIndex + 1);
                            anchor.setAnchorType(imageSettings.getAnchorType());
                            patriarch.createPicture(anchor, imageSettings.getIndex());
                        } catch (Exception ex) {
                            throw new JRException(EXCEPTION_MESSAGE_KEY_CANNOT_ADD_CELL, null, ex);
                        } catch (Error err) {
                            throw new JRException(EXCEPTION_MESSAGE_KEY_CANNOT_ADD_CELL, null, err);
                        }
                    }
                }

                if (cellSettings.getCellStyle() != null) {
                    cell.setCellStyle(cellSettings.getCellStyle());
                }
                if (cellSettings.getFormula() != null) {
                    // the formula text will be stored in formulaCellsMap in order to be applied only after 
                    // all defined names are created and available in the workbook (see #closeWorkbook())
                    formulaCellsMap.put(cell, cellSettings.getFormula());
                }
                if (cellSettings.getLink() != null) {
                    cell.setHyperlink(cellSettings.getLink());
                }
            }
        }
        ++rowIndex;
    }

    @Override
    protected void exportLine(JRPrintLine line) throws JRException {
        String currentColumnName = line.getPropertiesMap()
                .getProperty(JRXlsAbstractMetadataExporter.PROPERTY_COLUMN_NAME);

        if (currentColumnName != null && currentColumnName.length() > 0) {
            boolean repeatValue = getPropertiesUtil().getBooleanProperty(line,
                    JRXlsAbstractMetadataExporter.PROPERTY_REPEAT_VALUE, false);

            setColumnName(currentColumnName);
            adjustColumnWidth(currentColumnName, line.getWidth(),
                    ((JRXlsExporterNature) nature).getColumnAutoFit(line));
            adjustRowHeight(line.getHeight(), ((JRXlsExporterNature) nature).getRowAutoFit(line));

            short forecolor = getWorkbookColor(line.getLinePen().getLineColor()).getIndex();

            int side = BoxStyle.TOP;
            float ratio = line.getWidth() / line.getHeight();
            if (ratio > 1) {
                if (line.getDirectionValue() == LineDirectionEnum.TOP_DOWN) {
                    side = BoxStyle.TOP;
                } else {
                    side = BoxStyle.BOTTOM;
                }
            } else {
                if (line.getDirectionValue() == LineDirectionEnum.TOP_DOWN) {
                    side = BoxStyle.LEFT;
                } else {
                    side = BoxStyle.RIGHT;
                }
            }
            BoxStyle boxStyle = new BoxStyle(side, line.getLinePen());

            FillPatternType mode = backgroundMode;
            short backcolor = whiteIndex;
            if (!Boolean.TRUE.equals(sheetInfo.ignoreCellBackground) && line.getBackcolor() != null) {
                mode = FillPatternType.SOLID_FOREGROUND;
                backcolor = getWorkbookColor(line.getBackcolor()).getIndex();
            }

            HSSFCellStyle cellStyle = getLoadedCellStyle(mode, backcolor, HorizontalAlignment.LEFT,
                    VerticalAlignment.TOP, (short) 0, getLoadedFont(getDefaultFont(), forecolor, null, getLocale()),
                    boxStyle, isCellLocked(line), isCellHidden(line), isShrinkToFit(line));
            addBlankElement(cellStyle, repeatValue, currentColumnName);
        }
    }

    @Override
    protected void exportRectangle(JRPrintGraphicElement element) throws JRException {
        String currentColumnName = element.getPropertiesMap()
                .getProperty(JRXlsAbstractMetadataExporter.PROPERTY_COLUMN_NAME);

        if (currentColumnName != null && currentColumnName.length() > 0) {
            boolean repeatValue = getPropertiesUtil().getBooleanProperty(element,
                    JRXlsAbstractMetadataExporter.PROPERTY_REPEAT_VALUE, false);

            setColumnName(currentColumnName);
            adjustColumnWidth(currentColumnName, element.getWidth(),
                    ((JRXlsExporterNature) nature).getColumnAutoFit(element));
            adjustRowHeight(element.getHeight(), ((JRXlsExporterNature) nature).getRowAutoFit(element));

            short forecolor = getWorkbookColor(element.getLinePen().getLineColor()).getIndex();

            FillPatternType mode = backgroundMode;
            short backcolor = whiteIndex;
            if (!Boolean.TRUE.equals(sheetInfo.ignoreCellBackground) && element.getBackcolor() != null) {
                mode = FillPatternType.SOLID_FOREGROUND;
                backcolor = getWorkbookColor(element.getBackcolor()).getIndex();
            }

            HSSFCellStyle cellStyle = getLoadedCellStyle(mode, backcolor, HorizontalAlignment.LEFT,
                    VerticalAlignment.TOP, (short) 0, getLoadedFont(getDefaultFont(), forecolor, null, getLocale()),
                    new BoxStyle(element), isCellLocked(element), isCellHidden(element), isShrinkToFit(element));
            addBlankElement(cellStyle, repeatValue, currentColumnName);
        }
    }

    @Override
    protected void exportText(final JRPrintText textElement) throws JRException {
        String currentColumnName = textElement.getPropertiesMap()
                .getProperty(JRXlsAbstractMetadataExporter.PROPERTY_COLUMN_NAME);
        if (currentColumnName != null && currentColumnName.length() > 0) {
            final boolean hasCurrentColumnData = textElement.getPropertiesMap()
                    .containsProperty(JRXlsAbstractMetadataExporter.PROPERTY_DATA);
            String currentColumnData = textElement.getPropertiesMap()
                    .getProperty(JRXlsAbstractMetadataExporter.PROPERTY_DATA);
            boolean repeatValue = getPropertiesUtil().getBooleanProperty(textElement,
                    JRXlsAbstractMetadataExporter.PROPERTY_REPEAT_VALUE, false);

            setColumnName(currentColumnName);
            adjustColumnWidth(currentColumnName, textElement.getWidth(),
                    ((JRXlsExporterNature) nature).getColumnAutoFit(textElement));
            adjustRowHeight(textElement.getHeight(), isWrapText(textElement)
                    || Boolean.TRUE.equals(((JRXlsExporterNature) nature).getRowAutoFit(textElement)));

            final short forecolor = getWorkbookColor(textElement.getForecolor()).getIndex();

            TextAlignHolder textAlignHolder = getTextAlignHolder(textElement);
            HorizontalAlignment horizontalAlignment = getHorizontalAlignment(textAlignHolder);
            VerticalAlignment verticalAlignment = getVerticalAlignment(textAlignHolder);
            short rotation = getRotation(textAlignHolder);

            XlsReportConfiguration configuration = getCurrentItemConfiguration();

            FillPatternType mode = backgroundMode;
            short backcolor = whiteIndex;
            if (!Boolean.TRUE.equals(sheetInfo.ignoreCellBackground) && textElement.getBackcolor() != null) {
                mode = FillPatternType.SOLID_FOREGROUND;
                backcolor = getWorkbookColor(textElement.getBackcolor()).getIndex();
            }

            final StyleInfo baseStyle = isIgnoreTextFormatting(textElement)
                    ? new StyleInfo(mode, whiteIndex, horizontalAlignment, verticalAlignment, (short) 0, null,
                            (BoxStyle) null,
                            isWrapText(textElement) || Boolean.TRUE
                                    .equals(((JRXlsExporterNature) nature).getColumnAutoFit(textElement)),
                            isCellLocked(textElement), isCellHidden(textElement), isShrinkToFit(textElement))
                    : new StyleInfo(mode, backcolor, horizontalAlignment, verticalAlignment, rotation,
                            getLoadedFont(textElement, forecolor, null, getTextLocale(textElement)),
                            new BoxStyle(textElement),
                            isWrapText(textElement) || Boolean.TRUE
                                    .equals(((JRXlsExporterNature) nature).getColumnAutoFit(textElement)),
                            isCellLocked(textElement), isCellHidden(textElement), isShrinkToFit(textElement));

            final JRStyledText styledText;
            final String textStr;
            final String formula;
            final CellSettings cellSettings = new CellSettings();
            if (hasCurrentColumnData) {
                styledText = new JRStyledText();
                if (currentColumnData != null) {
                    styledText.append(currentColumnData);
                }
                textStr = currentColumnData;
                formula = null;
            } else {
                styledText = getStyledText(textElement);
                if (styledText != null) {
                    textStr = styledText.getText();
                } else {
                    textStr = null;
                }
                formula = getFormula(textElement);
            }

            if (formula != null) {
                TextValue value = getTextValue(textElement, textStr);

                if (value instanceof NumberTextValue) {
                    String convertedPattern = getConvertedPattern(textElement,
                            ((NumberTextValue) value).getPattern());
                    if (convertedPattern != null) {
                        baseStyle.setDataFormat(dataFormat.getFormat(convertedPattern));
                    }
                } else if (value instanceof DateTextValue) {
                    String convertedPattern = getConvertedPattern(textElement,
                            ((DateTextValue) value).getPattern());
                    if (convertedPattern != null) {
                        baseStyle.setDataFormat(dataFormat.getFormat(convertedPattern));
                    }
                }

                cellSettings.importValues(CellType.FORMULA, getLoadedCellStyle(baseStyle), null, formula);

            } else if (getCurrentItemConfiguration().isDetectCellType()) {
                TextValue value = getTextValue(textElement, textStr);
                value.handle(new TextValueHandler() {
                    @Override
                    public void handle(StringTextValue textValue) {
                        if (JRCommonText.MARKUP_NONE.equals(textElement.getMarkup())
                                || isIgnoreTextFormatting(textElement)) {
                            cellSettings.importValues(CellType.STRING, getLoadedCellStyle(baseStyle),
                                    new HSSFRichTextString(textValue.getText()));
                        } else {
                            cellSettings.importValues(CellType.STRING, getLoadedCellStyle(baseStyle),
                                    getRichTextString(styledText, forecolor, textElement,
                                            getTextLocale(textElement)));
                        }
                    }

                    @Override
                    public void handle(NumberTextValue textValue) {
                        String convertedPattern = getConvertedPattern(textElement, textValue.getPattern());
                        if (convertedPattern != null) {
                            baseStyle.setDataFormat(dataFormat.getFormat(convertedPattern));
                        }
                        Number value = null;
                        if (hasCurrentColumnData) {
                            if (textStr != null) {
                                try {
                                    value = Double.parseDouble(textStr);
                                } catch (NumberFormatException nfe) {
                                    throw new JRRuntimeException(nfe);
                                }
                            }
                        } else {
                            value = textValue.getValue();
                        }

                        if (value != null
                                && DefaultFormatFactory.STANDARD_NUMBER_FORMAT_DURATION.equals(convertedPattern)) {
                            value = value.doubleValue() / 86400;
                        }

                        cellSettings.importValues(CellType.NUMERIC, getLoadedCellStyle(baseStyle), value);
                    }

                    @Override
                    public void handle(DateTextValue textValue) {
                        String convertedPattern = getConvertedPattern(textElement, textValue.getPattern());
                        if (convertedPattern != null) {
                            baseStyle.setDataFormat(dataFormat.getFormat(convertedPattern));
                        }
                        Date value = null;
                        if (hasCurrentColumnData) {
                            if (textStr != null) {
                                try {
                                    value = new Date(Long.parseLong(textStr));
                                } catch (NumberFormatException nfe) {
                                    try {
                                        value = isoDateFormat.parse(textStr);
                                    } catch (ParseException pe) {
                                        throw new JRRuntimeException(pe);
                                    }
                                }
                            }
                        } else {
                            value = textValue.getValue() == null ? null
                                    : translateDateValue(textElement, textValue.getValue());
                        }
                        cellSettings.importValues(CellType.NUMERIC, getLoadedCellStyle(baseStyle), value);
                    }

                    @Override
                    public void handle(BooleanTextValue textValue) {
                        Boolean value = hasCurrentColumnData ? Boolean.valueOf(textStr) : textValue.getValue();
                        cellSettings.importValues(CellType.BOOLEAN, getLoadedCellStyle(baseStyle), value);
                    }
                });
            } else {
                if (JRCommonText.MARKUP_NONE.equals(textElement.getMarkup())
                        || isIgnoreTextFormatting(textElement)) {
                    cellSettings.importValues(CellType.STRING, getLoadedCellStyle(baseStyle),
                            new HSSFRichTextString(textStr));
                } else {
                    cellSettings.importValues(CellType.STRING, getLoadedCellStyle(baseStyle),
                            getRichTextString(styledText, forecolor, textElement, getTextLocale(textElement)));
                }
            }

            if (!configuration.isIgnoreAnchors()) {
                String anchorName = textElement.getAnchorName();
                if (anchorName != null) {
                    HSSFName aName = workbook.createName();
                    aName.setNameName(JRStringUtil.getJavaIdentifier(anchorName));
                    aName.setSheetIndex(workbook.getSheetIndex(sheet));
                    CellReference cRef = new CellReference(rowIndex, columnNamesMap.get(currentColumnName), true,
                            true);
                    aName.setRefersToFormula(cRef.formatAsString());
                    anchorNames.put(anchorName, aName);
                }
            }

            setHyperlinkCell(textElement, cellSettings);
            addTextElement(cellSettings, textStr, repeatValue, currentColumnName);
        }
    }

    protected void setHyperlinkCell(JRPrintHyperlink hyperlink, CellSettings cellSettings) {
        Hyperlink link = null;

        Boolean ignoreHyperlink = HyperlinkUtil.getIgnoreHyperlink(XlsReportConfiguration.PROPERTY_IGNORE_HYPERLINK,
                hyperlink);
        if (ignoreHyperlink == null) {
            ignoreHyperlink = getCurrentItemConfiguration().isIgnoreHyperlink();
        }

        if (!ignoreHyperlink) {
            JRHyperlinkProducer customHandler = getHyperlinkProducer(hyperlink);
            if (customHandler == null) {
                switch (hyperlink.getHyperlinkTypeValue()) {
                case REFERENCE: {
                    String href = hyperlink.getHyperlinkReference();
                    if (href != null) {
                        link = createHelper.createHyperlink(HyperlinkType.URL);
                        link.setAddress(href);
                    }
                    break;
                }
                case LOCAL_ANCHOR: {
                    if (!getCurrentItemConfiguration().isIgnoreAnchors()) {
                        String href = hyperlink.getHyperlinkAnchor();
                        if (href != null) {
                            link = createHelper.createHyperlink(HyperlinkType.DOCUMENT);
                            if (anchorLinks.containsKey(href)) {
                                (anchorLinks.get(href)).add(link);
                            } else {
                                List<Hyperlink> hrefList = new ArrayList<Hyperlink>();
                                hrefList.add(link);
                                anchorLinks.put(href, hrefList);
                            }
                        }
                    }
                    break;
                }
                case LOCAL_PAGE: {
                    Integer hrefPage = (getCurrentItemConfiguration().isOnePagePerSheet()
                            ? hyperlink.getHyperlinkPage()
                            : 0);
                    if (hrefPage != null) {
                        link = createHelper.createHyperlink(HyperlinkType.DOCUMENT);
                        if (pageLinks.containsKey(sheetsBeforeCurrentReport + hrefPage)) {
                            pageLinks.get(sheetsBeforeCurrentReport + hrefPage).add(link);
                        } else {
                            List<Hyperlink> hrefList = new ArrayList<Hyperlink>();
                            hrefList.add(link);
                            pageLinks.put(sheetsBeforeCurrentReport + hrefPage, hrefList);
                        }
                    }
                    break;
                }
                case REMOTE_ANCHOR: {
                    String href = hyperlink.getHyperlinkReference();
                    if (href != null && hyperlink.getHyperlinkAnchor() != null) {
                        href = href + "#" + hyperlink.getHyperlinkAnchor();
                        link = createHelper.createHyperlink(HyperlinkType.FILE);
                        link.setAddress(href);

                    }
                    break;
                }
                case REMOTE_PAGE: {
                    String href = hyperlink.getHyperlinkReference();
                    if (href != null && hyperlink.getHyperlinkPage() != null) {
                        href = href + "#JR_PAGE_ANCHOR_0_" + hyperlink.getHyperlinkPage().toString();
                        link = createHelper.createHyperlink(HyperlinkType.FILE);
                        link.setAddress(href);

                    }
                    break;
                }
                case NONE:
                default: {
                }
                }
            } else {
                String href = customHandler.getHyperlink(hyperlink);
                if (href != null) {
                    link = createHelper.createHyperlink(HyperlinkType.URL);
                    link.setAddress(href);
                }
            }

            if (link != null) {
                //TODO: make tooltips functional
                //            if(hyperlink.getHyperlinkTooltip() != null)
                //            {
                //               link.setLabel(hyperlink.getHyperlinkTooltip());
                //            }
                cellSettings.setLink(link);
            }
        }
    }

    protected void addTextElement(CellSettings cellSettings, String textStr, boolean repeatValue,
            String currentColumnName) throws JRException {
        if (columnNames.size() > 0) {
            if (columnNames.contains(currentColumnName) && !currentRow.containsKey(currentColumnName)
                    && isColumnReadOnTime(currentRow, currentColumnName)) { // the column is for export but was not read yet and comes in the expected order
                addCell(cellSettings, currentRow, currentColumnName);
            } else if ((columnNames.contains(currentColumnName) && !currentRow.containsKey(currentColumnName)
                    && !isColumnReadOnTime(currentRow, currentColumnName)) // the column is for export, was not read yet, but it is read after it should be
                    || (columnNames.contains(currentColumnName) && currentRow.containsKey(currentColumnName))) { // the column is for export and was already read

                if (rowIndex == 1 && getCurrentItemConfiguration().isWriteHeader()) {
                    writeReportHeader();
                }
                writeCurrentRow(currentRow, repeatedValues);
                currentRow.clear();
                addCell(cellSettings, currentRow, currentColumnName);
            }
            // set auto fill columns
            if (repeatValue) {
                if (currentColumnName != null && currentColumnName.length() > 0 && textStr.length() > 0) {
                    addCell(cellSettings, repeatedValues, currentColumnName);
                }
            } else {
                repeatedValues.remove(currentColumnName);
            }
        }
    }

    protected void addBlankElement(HSSFCellStyle cellStyle, boolean repeatValue, String currentColumnName)
            throws JRException {
        if (columnNames.size() > 0) {
            if (columnNames.contains(currentColumnName) && !currentRow.containsKey(currentColumnName)
                    && isColumnReadOnTime(currentRow, currentColumnName)) { // the column is for export but was not read yet and comes in the expected order
                addBlankCell(cellStyle, currentRow, currentColumnName);
            } else if ((columnNames.contains(currentColumnName) && !currentRow.containsKey(currentColumnName)
                    && !isColumnReadOnTime(currentRow, currentColumnName)) // the column is for export, was not read yet, but it is read after it should be
                    || (columnNames.contains(currentColumnName) && currentRow.containsKey(currentColumnName))) { // the column is for export and was already read

                if (rowIndex == 1 && getCurrentItemConfiguration().isWriteHeader()) {
                    writeReportHeader();
                }

                writeCurrentRow(currentRow, repeatedValues);
                currentRow.clear();
                addBlankCell(cellStyle, currentRow, currentColumnName);
            }

            // set auto fill columns
            if (repeatValue) {
                if (repeatValue && currentColumnName != null && currentColumnName.length() > 0
                        && cellStyle != null) {
                    addBlankCell(cellStyle, repeatedValues, currentColumnName);
                }
            } else {
                repeatedValues.remove(currentColumnName);
            }
        }
    }

    protected void addCell(CellSettings cellSettings, Map<String, Object> cellValueMap, String currentColumnName)
            throws JRException {
        cellValueMap.put(currentColumnName, cellSettings);
    }

    protected HSSFRichTextString getRichTextString(JRStyledText styledText, short forecolor, JRFont defaultFont,
            Locale locale) {
        String text = styledText.getText();
        HSSFRichTextString richTextStr = new HSSFRichTextString(text);
        int runLimit = 0;
        AttributedCharacterIterator iterator = styledText.getAttributedString().getIterator();

        while (runLimit < styledText.length() && (runLimit = iterator.getRunLimit()) <= styledText.length()) {
            Map<Attribute, Object> attributes = iterator.getAttributes();
            JRFont runFont = attributes.isEmpty() ? defaultFont : new JRBaseFont(attributes);
            short runForecolor = attributes.get(TextAttribute.FOREGROUND) != null
                    ? getWorkbookColor((Color) attributes.get(TextAttribute.FOREGROUND)).getIndex()
                    : forecolor;
            HSSFFont font = getLoadedFont(runFont, runForecolor, attributes, locale);
            richTextStr.applyFont(iterator.getIndex(), runLimit, font);
            iterator.setIndex(runLimit);
        }
        return richTextStr;
    }

    @Override
    public void exportImage(JRPrintImage element) throws JRException {

        String currentColumnName = element.getPropertiesMap()
                .getProperty(JRXlsAbstractMetadataExporter.PROPERTY_COLUMN_NAME);
        if (currentColumnName != null && currentColumnName.length() > 0) {
            boolean repeatValue = getPropertiesUtil().getBooleanProperty(element,
                    JRXlsAbstractMetadataExporter.PROPERTY_REPEAT_VALUE, false);

            setColumnName(currentColumnName);
            adjustColumnWidth(currentColumnName, element.getWidth(),
                    ((JRXlsExporterNature) nature).getColumnAutoFit(element));
            adjustRowHeight(element.getHeight(),
                    Boolean.TRUE.equals(((JRXlsExporterNature) nature).getRowAutoFit(element)));

            InternalImageProcessor imageProcessor = new InternalImageProcessor(element);

            Renderable renderer = element.getRenderer();

            if (renderer != null && imageProcessor.availableImageWidth > 0
                    && imageProcessor.availableImageHeight > 0) {
                InternalImageProcessorResult imageProcessorResult = null;

                try {
                    imageProcessorResult = imageProcessor.process(renderer);
                } catch (Exception e) {
                    Renderable onErrorRenderer = getRendererUtil().handleImageError(e,
                            element.getOnErrorTypeValue());
                    if (onErrorRenderer != null) {
                        imageProcessorResult = imageProcessor.process(onErrorRenderer);
                    }
                }

                if (imageProcessorResult != null)//FIXMEXLS background for null images like the other exporters
                {
                    XlsMetadataReportConfiguration configuration = getCurrentItemConfiguration();

                    FillPatternType mode = backgroundMode;
                    short backcolor = whiteIndex;
                    if (!Boolean.TRUE.equals(sheetInfo.ignoreCellBackground) && element.getBackcolor() != null) {
                        mode = FillPatternType.SOLID_FOREGROUND;
                        backcolor = getWorkbookColor(element.getBackcolor()).getIndex();
                    }

                    short forecolor = getWorkbookColor(element.getLineBox().getPen().getLineColor()).getIndex();

                    if (element.getModeValue() == ModeEnum.OPAQUE) {
                        backcolor = getWorkbookColor(element.getBackcolor()).getIndex();
                    }

                    HSSFCellStyle cellStyle = getLoadedCellStyle(mode, backcolor, HorizontalAlignment.LEFT,
                            VerticalAlignment.TOP, (short) 0,
                            getLoadedFont(getDefaultFont(), forecolor, null, getLocale()), new BoxStyle(element),
                            isCellLocked(element), isCellHidden(element), isShrinkToFit(element));

                    addBlankElement(cellStyle, false, currentColumnName);

                    int colIndex = columnNamesMap.get(currentColumnName);
                    try {
                        HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0, 0, 0, (short) (colIndex), rowIndex,
                                (short) (colIndex + 1), rowIndex + 1);
                        ImageAnchorTypeEnum imageAnchorType = ImageAnchorTypeEnum.getByName(JRPropertiesUtil
                                .getOwnProperty(element, XlsReportConfiguration.PROPERTY_IMAGE_ANCHOR_TYPE));
                        if (imageAnchorType == null) {
                            imageAnchorType = configuration.getImageAnchorType();
                            if (imageAnchorType == null) {
                                imageAnchorType = ImageAnchorTypeEnum.MOVE_NO_SIZE;
                            }
                        }
                        anchor.setAnchorType(JRXlsExporter.getAnchorType(imageAnchorType));
                        //pngEncoder.setImage(bi);
                        //int imgIndex = workbook.addPicture(pngEncoder.pngEncode(), HSSFWorkbook.PICTURE_TYPE_PNG);
                        int imgIndex = workbook.addPicture(imageProcessorResult.imageData,
                                HSSFWorkbook.PICTURE_TYPE_PNG);
                        patriarch.createPicture(anchor, imgIndex);

                        // set auto fill columns
                        if (repeatValue) {
                            CellSettings cellSettings = new CellSettings(cellStyle);
                            cellSettings.setCellValue(new ImageSettings(imgIndex, anchor.getAnchorType()));
                            addCell(cellSettings, repeatedValues, currentColumnName);
                        } else {
                            repeatedValues.remove(currentColumnName);
                        }

                        //                  setHyperlinkCell(element);
                    } catch (Exception ex) {
                        throw new JRException(EXCEPTION_MESSAGE_KEY_CANNOT_ADD_CELL, null, ex);
                    } catch (Error err) {
                        throw new JRException(EXCEPTION_MESSAGE_KEY_CANNOT_ADD_CELL, null, err);
                    }
                }
            }
        }
    }

    private class InternalImageProcessor {
        private final JRPrintImage imageElement;
        private final RenderersCache imageRenderersCache;

        private final int topPadding;
        private final int leftPadding;
        private final int bottomPadding;
        private final int rightPadding;
        private final int availableImageWidth;
        private final int availableImageHeight;

        protected InternalImageProcessor(JRPrintImage imageElement) {
            this.imageElement = imageElement;
            this.imageRenderersCache = imageElement.isUsingCache() ? renderersCache
                    : new RenderersCache(getJasperReportsContext());

            topPadding = Math.max(imageElement.getLineBox().getTopPadding(),
                    getImageBorderCorrection(imageElement.getLineBox().getTopPen()));
            leftPadding = Math.max(imageElement.getLineBox().getLeftPadding(),
                    getImageBorderCorrection(imageElement.getLineBox().getLeftPen()));
            bottomPadding = Math.max(imageElement.getLineBox().getBottomPadding(),
                    getImageBorderCorrection(imageElement.getLineBox().getBottomPen()));
            rightPadding = Math.max(imageElement.getLineBox().getRightPadding(),
                    getImageBorderCorrection(imageElement.getLineBox().getRightPen()));

            int tmpAvailableImageWidth = imageElement.getWidth() - leftPadding - rightPadding;
            availableImageWidth = tmpAvailableImageWidth < 0 ? 0 : tmpAvailableImageWidth;

            int tmpAvailableImageHeight = imageElement.getHeight() - topPadding - bottomPadding;
            availableImageHeight = tmpAvailableImageHeight < 0 ? 0 : tmpAvailableImageHeight;
        }

        private InternalImageProcessorResult process(Renderable renderer) throws JRException {
            InternalImageProcessorResult imageProcessorResult = null;

            if (renderer instanceof ResourceRenderer) {
                renderer = imageRenderersCache.getLoadedRenderer((ResourceRenderer) renderer);
            }

            switch (imageElement.getScaleImageValue()) {
            case CLIP: {
                imageProcessorResult = processImageClip(imageRenderersCache.getGraphics2DRenderable(renderer));

                break;
            }
            case FILL_FRAME: {
                imageProcessorResult = processImageFillFrame(getRendererUtil().getImageDataRenderable(
                        imageRenderersCache, renderer, new Dimension(availableImageWidth, availableImageHeight),
                        ModeEnum.OPAQUE == imageElement.getModeValue() ? imageElement.getBackcolor() : null));

                break;
            }
            case RETAIN_SHAPE:
            default: {
                imageProcessorResult = processImageRetainShape(getRendererUtil().getImageDataRenderable(
                        imageRenderersCache, renderer, new Dimension(availableImageWidth, availableImageHeight),
                        ModeEnum.OPAQUE == imageElement.getModeValue() ? imageElement.getBackcolor() : null));

                break;
            }
            }

            return imageProcessorResult;
        }

        private InternalImageProcessorResult processImageClip(Graphics2DRenderable renderer) throws JRException {
            int normalWidth = availableImageWidth;
            int normalHeight = availableImageHeight;

            Dimension2D dimension = renderer instanceof DimensionRenderable
                    ? ((DimensionRenderable) renderer).getDimension(jasperReportsContext)
                    : null;
            if (dimension != null) {
                normalWidth = (int) dimension.getWidth();
                normalHeight = (int) dimension.getHeight();
            }

            int dpi = getPropertiesUtil().getIntegerProperty(Renderable.PROPERTY_IMAGE_DPI, 72);
            double scale = dpi / 72d;

            BufferedImage bi = new BufferedImage((int) (scale * availableImageWidth),
                    (int) (scale * availableImageHeight), BufferedImage.TYPE_INT_ARGB);

            Graphics2D grx = bi.createGraphics();
            try {
                grx.scale(scale, scale);
                grx.clip(new Rectangle(0, 0, availableImageWidth, availableImageHeight));

                renderer.render(jasperReportsContext, grx, new Rectangle(
                        (int) (ImageUtil.getXAlignFactor(imageElement) * (availableImageWidth - normalWidth)),
                        (int) (ImageUtil.getYAlignFactor(imageElement) * (availableImageHeight - normalHeight)),
                        normalWidth, normalHeight));
            } finally {
                grx.dispose();
            }

            byte[] imageData = null;
            int topOffset = 0;
            int leftOffset = 0;
            int bottomOffset = 0;
            int rightOffset = 0;

            //         topOffset = topPadding;
            //         leftOffset = leftPadding;
            //         bottomOffset = bottomPadding;
            //         rightOffset = rightPadding;

            imageData = JRImageLoader.getInstance(jasperReportsContext).loadBytesFromAwtImage(bi,
                    ImageTypeEnum.PNG);

            return new InternalImageProcessorResult(imageData, topOffset, leftOffset, bottomOffset, rightOffset);
        }

        private InternalImageProcessorResult processImageFillFrame(DataRenderable renderer) throws JRException {
            int topOffset = 0;
            int leftOffset = 0;
            int bottomOffset = 0;
            int rightOffset = 0;

            //         topOffset = topPadding;
            //         leftOffset = leftPadding;
            //         bottomOffset = bottomPadding;
            //         rightOffset = rightPadding;

            return new InternalImageProcessorResult(renderer.getData(jasperReportsContext), topOffset, leftOffset,
                    bottomOffset, rightOffset);
        }

        private InternalImageProcessorResult processImageRetainShape(DataRenderable renderer) throws JRException {
            int normalWidth = availableImageWidth;
            int normalHeight = availableImageHeight;

            Dimension2D dimension = renderer instanceof DimensionRenderable
                    ? ((DimensionRenderable) renderer).getDimension(jasperReportsContext)
                    : null;
            if (dimension != null) {
                normalWidth = (int) dimension.getWidth();
                normalHeight = (int) dimension.getHeight();
            }

            double ratio = (double) normalWidth / (double) normalHeight;

            if (ratio > (double) availableImageWidth / (double) availableImageHeight) {
                normalWidth = availableImageWidth;
                normalHeight = (int) (availableImageWidth / ratio);
            } else {
                normalWidth = (int) (availableImageHeight * ratio);
                normalHeight = availableImageHeight;
            }

            int topOffset = 0;
            int leftOffset = 0;
            int bottomOffset = 0;
            int rightOffset = 0;

            //         topOffset = topPadding + (int) (yalignFactor * (availableImageHeight - normalHeight));
            //         leftOffset = leftPadding + (int) (xalignFactor * (availableImageWidth - normalWidth));
            //         bottomOffset = bottomPadding + (int) ((1f - yalignFactor) * (availableImageHeight - normalHeight));
            //         rightOffset = rightPadding + (int) ((1f - xalignFactor) * (availableImageWidth - normalWidth));

            return new InternalImageProcessorResult(renderer.getData(jasperReportsContext), topOffset, leftOffset,
                    bottomOffset, rightOffset);
        }
    }

    private class InternalImageProcessorResult {
        private final byte[] imageData;
        //      private final int topOffset;
        //      private final int leftOffset;
        //      private final int bottomOffset;
        //      private final int rightOffset;

        protected InternalImageProcessorResult(byte[] imageData, int topOffset, int leftOffset, int bottomOffset,
                int rightOffset) {
            this.imageData = imageData;
            //         this.topOffset = topOffset;
            //         this.leftOffset = leftOffset;
            //         this.bottomOffset = bottomOffset;
            //         this.rightOffset = rightOffset;
        }
    }

    protected HSSFCellStyle getLoadedCellStyle(StyleInfo style) {
        HSSFCellStyle cellStyle = loadedCellStyles.get(style);
        if (cellStyle == null) {

            cellStyle = workbook.createCellStyle();
            cellStyle.setFillForegroundColor(style.backcolor);
            cellStyle.setFillPattern(style.mode);
            cellStyle.setAlignment(style.horizontalAlignment);
            cellStyle.setVerticalAlignment(style.verticalAlignment);
            cellStyle.setRotation(style.rotation);
            if (style.font != null) {
                cellStyle.setFont(style.font);
            }
            cellStyle.setWrapText(style.lcWrapText);
            cellStyle.setLocked(style.lcCellLocked);
            cellStyle.setHidden(style.lcCellHidden);
            cellStyle.setShrinkToFit(style.lcShrinkToFit);

            if (style.hasDataFormat()) {
                cellStyle.setDataFormat(style.getDataFormat());
            }

            if (!Boolean.TRUE.equals(sheetInfo.ignoreCellBorder) && style.box != null) {
                BoxStyle box = style.box;
                if (box.borderStyle[BoxStyle.TOP] != null)
                    cellStyle.setBorderTop(box.borderStyle[BoxStyle.TOP]);
                cellStyle.setTopBorderColor(box.borderColour[BoxStyle.TOP]);
                if (box.borderStyle[BoxStyle.LEFT] != null)
                    cellStyle.setBorderLeft(box.borderStyle[BoxStyle.LEFT]);
                cellStyle.setLeftBorderColor(box.borderColour[BoxStyle.LEFT]);
                if (box.borderStyle[BoxStyle.BOTTOM] != null)
                    cellStyle.setBorderBottom(box.borderStyle[BoxStyle.BOTTOM]);
                cellStyle.setBottomBorderColor(box.borderColour[BoxStyle.BOTTOM]);
                if (box.borderStyle[BoxStyle.RIGHT] != null)
                    cellStyle.setBorderRight(box.borderStyle[BoxStyle.RIGHT]);
                cellStyle.setRightBorderColor(box.borderColour[BoxStyle.RIGHT]);
            }
            loadedCellStyles.put(style, cellStyle);
        }
        return cellStyle;
    }

    protected HSSFCellStyle getLoadedCellStyle(FillPatternType mode, short backcolor,
            HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment, short rotation,
            HSSFFont font, BoxStyle box, boolean isCellLocked, boolean isCellHidden, boolean isShrinkToFit) {
        return getLoadedCellStyle(new StyleInfo(mode, backcolor, horizontalAlignment, verticalAlignment, rotation,
                font, box, true, isCellLocked, isCellHidden, isShrinkToFit));
    }

    /**
     *
     */
    protected static BorderStyle getBorderStyle(JRPen pen) {
        float lineWidth = pen.getLineWidth();

        if (lineWidth > 0f) {
            switch (pen.getLineStyleValue()) {
            case DOUBLE: {
                return BorderStyle.DOUBLE;
            }
            case DOTTED: {
                return BorderStyle.DOTTED;
            }
            case DASHED: {
                if (lineWidth >= 1f) {
                    return BorderStyle.MEDIUM_DASHED;
                }
                return BorderStyle.DASHED;
            }
            case SOLID:
            default: {
                if (lineWidth >= 2f) {
                    return BorderStyle.THICK;
                } else if (lineWidth >= 1f) {
                    return BorderStyle.MEDIUM;
                } else if (lineWidth >= 0.5f) {
                    return BorderStyle.THIN;
                }
                return BorderStyle.HAIR;
            }
            }
        }
        return BorderStyle.NONE;
    }

    @Override
    protected void exportFrame(JRPrintFrame frame) throws JRException {

        for (Object element : frame.getElements()) {
            if (element instanceof JRPrintLine) {
                exportLine((JRPrintLine) element);
            } else if (element instanceof JRPrintRectangle) {
                exportRectangle((JRPrintRectangle) element);
            } else if (element instanceof JRPrintEllipse) {
                exportRectangle((JRPrintEllipse) element);
            } else if (element instanceof JRPrintImage) {
                exportImage((JRPrintImage) element);
            } else if (element instanceof JRPrintText) {
                exportText((JRPrintText) element);
            } else if (element instanceof JRPrintFrame) {
                exportFrame((JRPrintFrame) element);
            } else if (element instanceof JRGenericPrintElement) {
                exportGenericElement((JRGenericPrintElement) element);
            }
        }
    }

    @Override
    protected void exportGenericElement(JRGenericPrintElement element) throws JRException {
        String currentColumnName = element.getPropertiesMap()
                .getProperty(JRXlsAbstractMetadataExporter.PROPERTY_COLUMN_NAME);
        if (currentColumnName != null && currentColumnName.length() > 0) {
            boolean repeatValue = getPropertiesUtil().getBooleanProperty(element,
                    JRXlsAbstractMetadataExporter.PROPERTY_REPEAT_VALUE, false);
            int colIndex = columnNamesMap.get(currentColumnName);

            setColumnName(currentColumnName);
            adjustColumnWidth(currentColumnName, element.getWidth(),
                    ((JRXlsExporterNature) nature).getColumnAutoFit(element));
            adjustRowHeight(element.getHeight(),
                    Boolean.TRUE.equals(((JRXlsExporterNature) nature).getRowAutoFit(element)));

            GenericElementXlsMetadataHandler handler = (GenericElementXlsMetadataHandler) GenericElementHandlerEnviroment
                    .getInstance(getJasperReportsContext())
                    .getElementHandler(element.getGenericType(), XLS_EXPORTER_KEY);

            if (handler != null) {
                rowIndex = handler.exportElement(exporterContext, element, currentRow, repeatedValues, columnNames,
                        columnNamesMap, currentColumnName, colIndex, rowIndex, repeatValue);
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("No XLS Metadata generic element handler for " + element.getGenericType());
                }
            }
        }
    }

    @Override
    protected ExporterNature getNature() {
        return nature;
    }

    @Override
    public String getExporterKey() {
        return XLS_EXPORTER_KEY;
    }

    @Override
    public String getExporterPropertiesPrefix() {
        return XLS_EXPORTER_PROPERTIES_PREFIX;
    }

    protected void setColumnName(String currentColumnName) {
        // when no columns are provided, build the column names list as they are retrieved from the report element property
        if (!hasDefinedColumns) {
            if (currentColumnName != null && currentColumnName.length() > 0
                    && !columnNames.contains(currentColumnName)) {
                columnNamesMap.put(currentColumnName, columnNames.size());
                columnNames.add(currentColumnName);
            }
        }
    }

    /**
     * Writes the header column names
     */
    @Override
    protected void writeReportHeader() throws JRException {
        row = sheet.createRow(0);
        for (int i = 0; i < columnNames.size(); i++) {
            String columnName = columnNames.get(i);
            cell = row.createCell(i);
            cell.setCellType(CellType.STRING);
            cell.setCellValue(new HSSFRichTextString(columnName));
        }
    }

    @Override
    protected void setSheetName(String sheetName) {
        workbook.setSheetName(workbook.getSheetIndex(sheet), sheetName);
    }

    @Override
    protected void setFreezePane(int rowIndex, int colIndex) {
        if (rowIndex > 0 || colIndex > 0) {
            sheet.createFreezePane(Math.max(0, colIndex), Math.max(0, rowIndex));
        }
    }

    @Override
    protected void setAutoFilter(String autoFilterRange) {
        //TODO: recalculate autoFilterRange depending on exported columns
        //      sheet.setAutoFilter(CellRangeAddress.valueOf(autoFilterRange));
    }

    @Override
    protected void setRowLevels(XlsRowLevelInfo levelInfo, String level) {
        //TODO: recalculate row levels depending on exported columns
        //      if(levelInfo != null) {
        //         Map<String, Integer> levelMap = levelInfo.getLevelMap();
        //         if(levelMap != null && levelMap.size() > 0) {
        //            for(String l : levelMap.keySet()) {
        //               if (level == null || l.compareTo(level) >= 0) {
        //                  Integer startIndex = levelMap.get(l);
        //                  if(levelInfo.getEndIndex() >= startIndex) {
        //                     sheet.groupRow(startIndex, levelInfo.getEndIndex());
        //                  }
        //               }
        //            }
        //            sheet.setRowSumsBelow(false);
        //         }
        //      }
    }

    private final short getSuitablePaperSize() {

        if (pageFormat == null) {
            return -1;
        }
        long width = 0;
        long height = 0;
        short ps = -1;

        if ((pageFormat.getPageWidth() != 0) && (pageFormat.getPageHeight() != 0)) {

            double dWidth = (pageFormat.getPageWidth() / 72.0);
            double dHeight = (pageFormat.getPageHeight() / 72.0);

            height = Math.round(dHeight * 25.4);
            width = Math.round(dWidth * 25.4);

            // Compare to ISO 216 A-Series (A3-A5). All other ISO 216 formats
            // not supported by POI Api yet.
            // A3 papersize also not supported by POI Api yet.
            for (int i = 4; i < 6; i++) {
                int w = calculateWidthForDinAN(i);
                int h = calculateHeightForDinAN(i);

                if (((w == width) && (h == height)) || ((h == width) && (w == height))) {
                    if (i == 4) {
                        ps = HSSFPrintSetup.A4_PAPERSIZE;
                    } else if (i == 5) {
                        ps = HSSFPrintSetup.A5_PAPERSIZE;
                    }
                    break;
                }
            }

            //envelope sizes
            if (ps == -1) {
                // ISO 269 sizes - "Envelope DL" (110 x 220 mm)
                if (((width == 110) && (height == 220)) || ((width == 220) && (height == 110))) {
                    ps = HSSFPrintSetup.ENVELOPE_DL_PAPERSIZE;
                }
            }

            // Compare to common North American Paper Sizes (ANSI X3.151-1987).
            if (ps == -1) {
                // ANSI X3.151-1987 - "Letter" (216 x 279 mm)
                if (((width == 216) && (height == 279)) || ((width == 279) && (height == 216))) {
                    ps = HSSFPrintSetup.LETTER_PAPERSIZE;
                }
                // ANSI X3.151-1987 - "Legal" (216 x 356 mm)
                if (((width == 216) && (height == 356)) || ((width == 356) && (height == 216))) {
                    ps = HSSFPrintSetup.LEGAL_PAPERSIZE;
                }
                // ANSI X3.151-1987 - "Executive" (190 x 254 mm)
                else if (((width == 190) && (height == 254)) || ((width == 254) && (height == 190))) {
                    ps = HSSFPrintSetup.EXECUTIVE_PAPERSIZE;
                }
                // ANSI X3.151-1987 - "Ledger/Tabloid" (279 x 432 mm)
                // Not supported by POI Api yet.
            }
        }
        return ps;
    }

    private HorizontalAlignment getHorizontalAlignment(TextAlignHolder alignment) {
        switch (alignment.horizontalAlignment) {
        case RIGHT:
            return HorizontalAlignment.RIGHT;
        case CENTER:
            return HorizontalAlignment.CENTER;
        case JUSTIFIED:
            return HorizontalAlignment.JUSTIFY;
        case LEFT:
        default:
            return HorizontalAlignment.LEFT;
        }
    }

    private VerticalAlignment getVerticalAlignment(TextAlignHolder alignment) {
        switch (alignment.verticalAlignment) {
        case BOTTOM:
            return VerticalAlignment.BOTTOM;
        case MIDDLE:
            return VerticalAlignment.CENTER;
        case JUSTIFIED:
            return VerticalAlignment.JUSTIFY;
        case TOP:
        default:
            return VerticalAlignment.TOP;
        }
    }

    private short getRotation(TextAlignHolder alignment) {
        switch (alignment.rotation) {
        case LEFT:
            return 90;
        case RIGHT:
            return -90;
        case UPSIDE_DOWN:
        case NONE:
        default:
            return 0;
        }
    }

    /**
     *
     */
    protected HSSFColor getWorkbookColor(Color awtColor) {
        HSSFColor color = null;
        if (awtColor != null) {
            byte red = (byte) awtColor.getRed();
            byte green = (byte) awtColor.getGreen();
            byte blue = (byte) awtColor.getBlue();

            XlsExporterConfiguration configuration = getCurrentConfiguration();

            if (configuration.isCreateCustomPalette()) {
                try {
                    color = palette.findColor(red, green, blue) != null ? palette.findColor(red, green, blue)
                            : palette.addColor(red, green, blue);
                } catch (Exception e) {
                    if (customColorIndex < MAX_COLOR_INDEX) {
                        palette.setColorAtIndex(customColorIndex, red, green, blue);
                        color = palette.getColor(customColorIndex++);
                    } else {
                        color = palette.findSimilarColor(red, green, blue);
                    }
                }
            }
        }
        return color == null ? getNearestColor(awtColor) : color;
    }

    /**
     *
     */
    protected static HSSFColor getNearestColor(Color awtColor) {
        HSSFColor color = hssfColorsCache.get(awtColor);
        if (color == null) {
            Map<?, ?> triplets = HSSFColor.getTripletHash();
            if (triplets != null) {
                Collection<?> keys = triplets.keySet();
                if (keys != null && keys.size() > 0) {
                    Object key = null;
                    HSSFColor crtColor = null;
                    short[] rgb = null;
                    int diff = 0;
                    int minDiff = 999;
                    for (Iterator<?> it = keys.iterator(); it.hasNext();) {
                        key = it.next();
                        crtColor = (HSSFColor) triplets.get(key);
                        rgb = crtColor.getTriplet();
                        diff = Math.abs(rgb[0] - awtColor.getRed()) + Math.abs(rgb[1] - awtColor.getGreen())
                                + Math.abs(rgb[2] - awtColor.getBlue());
                        if (diff < minDiff) {
                            minDiff = diff;
                            color = crtColor;
                        }
                    }
                }
            }
            hssfColorsCache.put(awtColor, color);
        }
        return color;
    }

    /**
     *
     */
    protected HSSFFont getLoadedFont(JRFont font, short forecolor, Map<Attribute, Object> attributes,
            Locale locale) {

        HSSFFont cellFont = null;
        String fontName = fontUtil.getExportFontFamily(font.getFontName(), locale, getExporterKey());

        boolean isFontSizeFixEnabled = getCurrentItemConfiguration().isFontSizeFixEnabled();

        short superscriptType = HSSFFont.SS_NONE;

        if (attributes != null && attributes.get(TextAttribute.SUPERSCRIPT) != null) {
            Object value = attributes.get(TextAttribute.SUPERSCRIPT);
            if (TextAttribute.SUPERSCRIPT_SUPER.equals(value)) {
                superscriptType = HSSFFont.SS_SUPER;
            } else if (TextAttribute.SUPERSCRIPT_SUB.equals(value)) {
                superscriptType = HSSFFont.SS_SUB;
            }
        }
        for (int i = 0; i < loadedFonts.size(); i++) {
            HSSFFont cf = (HSSFFont) loadedFonts.get(i);
            short fontSize = (short) font.getFontsize();
            if (isFontSizeFixEnabled) {
                fontSize -= 1;
            }
            if (cf.getFontName().equals(fontName) && (cf.getColor() == forecolor)
                    && (cf.getFontHeightInPoints() == fontSize)
                    && ((cf.getUnderline() == HSSFFont.U_SINGLE) ? (font.isUnderline()) : (!font.isUnderline()))
                    && (cf.getStrikeout() == font.isStrikeThrough()) && (cf.getBold() == font.isBold())
                    && (cf.getItalic() == font.isItalic()) && (cf.getTypeOffset() == superscriptType)) {
                cellFont = cf;
                break;
            }
        }

        if (cellFont == null) {

            cellFont = workbook.createFont();
            cellFont.setFontName(fontName);
            cellFont.setColor(forecolor);
            short fontSize = (short) font.getFontsize();
            if (isFontSizeFixEnabled) {
                fontSize -= 1;
            }
            cellFont.setFontHeightInPoints(fontSize);

            if (font.isUnderline()) {
                cellFont.setUnderline(HSSFFont.U_SINGLE);
            }
            if (font.isStrikeThrough()) {
                cellFont.setStrikeout(true);
            }
            if (font.isBold()) {
                cellFont.setBold(true);
            }
            if (font.isItalic()) {
                cellFont.setItalic(true);
            }

            cellFont.setTypeOffset(superscriptType);
            loadedFonts.add(cellFont);
        }
        return cellFont;
    }

    @Override
    protected void createSheet(CutsInfo xCuts, SheetInfo sheetInfo) {
    }

    @Override
    protected void setRowHeight(int rowIndex, int lastRowHeight, Cut yCut, XlsRowLevelInfo levelInfo)
            throws JRException {
    }

    @Override
    protected void addRowBreak(int rowIndex) {
        sheet.setRowBreak(rowIndex);
    }

    @Override
    protected void setColumnWidth(int col, int width, boolean autoFit) {
    }

    @Override
    protected void updateSheet(JRPrintElement element) {
        JRXlsMetadataExporterNature xlsNature = (JRXlsMetadataExporterNature) nature;
        configureDefinedNames(xlsNature, element);
        updatePageMargin(xlsNature.getPrintPageTopMargin(element), Sheet.TopMargin);
        updatePageMargin(xlsNature.getPrintPageLeftMargin(element), Sheet.LeftMargin);
        updatePageMargin(xlsNature.getPrintPageBottomMargin(element), Sheet.BottomMargin);
        updatePageMargin(xlsNature.getPrintPageRightMargin(element), Sheet.RightMargin);
        updateHeaderFooterMargin(xlsNature.getPrintHeaderMargin(element), true);
        updateHeaderFooterMargin(xlsNature.getPrintFooterMargin(element), false);

        HSSFHeader header = sheet.getHeader();
        String sheetHeaderLeft = xlsNature.getSheetHeaderLeft(element);
        if (sheetHeaderLeft != null) {
            header.setLeft(sheetHeaderLeft);
        }

        String sheetHeaderCenter = xlsNature.getSheetHeaderCenter(element);
        if (sheetHeaderCenter != null) {
            header.setCenter(sheetHeaderCenter);
        }

        String sheetHeaderRight = xlsNature.getSheetHeaderRight(element);
        if (sheetHeaderRight != null) {
            header.setRight(sheetHeaderRight);
        }

        HSSFFooter footer = sheet.getFooter();
        String sheetFooterLeft = xlsNature.getSheetFooterLeft(element);
        if (sheetFooterLeft != null) {
            footer.setLeft(sheetFooterLeft);
        }

        String sheetFooterCenter = xlsNature.getSheetFooterCenter(element);
        if (sheetFooterCenter != null) {
            footer.setCenter(sheetFooterCenter);
        }

        String sheetFooterRight = xlsNature.getSheetFooterRight(element);
        if (sheetFooterRight != null) {
            footer.setRight(sheetFooterRight);
        }
    }

    private void updatePageMargin(Integer marginValue, short marginType) {
        if (marginValue != null) {
            double margin = LengthUtil.inch(marginValue);
            if (margin > sheet.getMargin(marginType)) {
                sheet.setMargin(marginType, margin);
            }
        }
    }

    private void updateHeaderFooterMargin(Integer marginValue, boolean isHeaderMargin) {
        if (marginValue != null) {
            HSSFPrintSetup printSetup = sheet.getPrintSetup();
            double margin = LengthUtil.inch(marginValue);
            if (isHeaderMargin) {
                if (margin > printSetup.getHeaderMargin()) {
                    printSetup.setHeaderMargin(margin);
                }
            } else {
                if (margin > printSetup.getFooterMargin()) {
                    printSetup.setFooterMargin(margin);
                }
            }
        }
    }

    /**
     * 
     */
    protected class BoxStyle {
        protected static final int TOP = 0;
        protected static final int LEFT = 1;
        protected static final int BOTTOM = 2;
        protected static final int RIGHT = 3;

        protected BorderStyle[] borderStyle = new BorderStyle[] { BorderStyle.NONE, BorderStyle.NONE,
                BorderStyle.NONE, BorderStyle.NONE };
        protected short[] borderColour = new short[4];
        private int hash;

        public BoxStyle(int side, JRPen pen) {
            borderStyle[side] = JRXlsMetadataExporter.getBorderStyle(pen);
            borderColour[side] = JRXlsMetadataExporter.this.getWorkbookColor(pen.getLineColor()).getIndex();
            hash = computeHash();
        }

        public BoxStyle(JRPrintElement element) {
            if (element != null) {
                if (element instanceof JRBoxContainer) {
                    setBox(((JRBoxContainer) element).getLineBox());
                }
                if (element instanceof JRCommonGraphicElement) {
                    setPen(((JRCommonGraphicElement) element).getLinePen());
                }
            }
        }

        public void setBox(JRLineBox box) {
            borderStyle[TOP] = JRXlsMetadataExporter.getBorderStyle(box.getTopPen());
            borderColour[TOP] = JRXlsMetadataExporter.this.getWorkbookColor(box.getTopPen().getLineColor())
                    .getIndex();

            borderStyle[BOTTOM] = JRXlsMetadataExporter.getBorderStyle(box.getBottomPen());
            borderColour[BOTTOM] = JRXlsMetadataExporter.this.getWorkbookColor(box.getBottomPen().getLineColor())
                    .getIndex();

            borderStyle[LEFT] = JRXlsMetadataExporter.getBorderStyle(box.getLeftPen());
            borderColour[LEFT] = JRXlsMetadataExporter.this.getWorkbookColor(box.getLeftPen().getLineColor())
                    .getIndex();

            borderStyle[RIGHT] = JRXlsMetadataExporter.getBorderStyle(box.getRightPen());
            borderColour[RIGHT] = JRXlsMetadataExporter.this.getWorkbookColor(box.getRightPen().getLineColor())
                    .getIndex();

            hash = computeHash();
        }

        public void setPen(JRPen pen) {
            if (borderStyle[TOP] == BorderStyle.NONE && borderStyle[LEFT] == BorderStyle.NONE
                    && borderStyle[BOTTOM] == BorderStyle.NONE && borderStyle[RIGHT] == BorderStyle.NONE) {
                BorderStyle style = JRXlsMetadataExporter.getBorderStyle(pen);
                short colour = JRXlsMetadataExporter.this.getWorkbookColor(pen.getLineColor()).getIndex();

                borderStyle[TOP] = style;
                borderStyle[BOTTOM] = style;
                borderStyle[LEFT] = style;
                borderStyle[RIGHT] = style;

                borderColour[TOP] = colour;
                borderColour[BOTTOM] = colour;
                borderColour[LEFT] = colour;
                borderColour[RIGHT] = colour;
            }

            hash = computeHash();
        }

        private int computeHash() {
            int hashCode = (borderStyle[TOP] == null ? 0 : borderStyle[TOP].hashCode());
            hashCode = 31 * hashCode + borderColour[TOP];
            hashCode = 31 * hashCode + (borderStyle[BOTTOM] == null ? 0 : borderStyle[BOTTOM].hashCode());
            hashCode = 31 * hashCode + borderColour[BOTTOM];
            hashCode = 31 * hashCode + (borderStyle[LEFT] == null ? 0 : borderStyle[LEFT].hashCode());
            hashCode = 31 * hashCode + borderColour[LEFT];
            hashCode = 31 * hashCode + (borderStyle[RIGHT] == null ? 0 : borderStyle[RIGHT].hashCode());
            hashCode = 31 * hashCode + borderColour[RIGHT];
            return hashCode;
        }

        @Override
        public int hashCode() {
            return hash;
        }

        @Override
        public boolean equals(Object o) {
            BoxStyle b = (BoxStyle) o;

            return b.borderStyle[TOP] == borderStyle[TOP] && b.borderColour[TOP] == borderColour[TOP]
                    && b.borderStyle[BOTTOM] == borderStyle[BOTTOM]
                    && b.borderColour[BOTTOM] == borderColour[BOTTOM] && b.borderStyle[LEFT] == borderStyle[LEFT]
                    && b.borderColour[LEFT] == borderColour[LEFT] && b.borderStyle[RIGHT] == borderStyle[RIGHT]
                    && b.borderColour[RIGHT] == borderColour[RIGHT];
        }

        @Override
        public String toString() {
            return "(" + borderStyle[TOP] + "/" + borderColour[TOP] + "," + borderStyle[BOTTOM] + "/"
                    + borderColour[BOTTOM] + "," + borderStyle[LEFT] + "/" + borderColour[LEFT] + ","
                    + borderStyle[RIGHT] + "/" + borderColour[RIGHT] + ")";
        }
    }

    /**
     * 
     */
    protected class StyleInfo {
        protected final FillPatternType mode;
        protected final short backcolor;
        protected final HorizontalAlignment horizontalAlignment;
        protected final VerticalAlignment verticalAlignment;
        protected final short rotation;
        protected final HSSFFont font;
        protected final BoxStyle box;
        protected final boolean lcWrapText;
        protected final boolean lcCellLocked;
        protected final boolean lcCellHidden;
        protected final boolean lcShrinkToFit;
        private short lcDataFormat = -1;
        private int hashCode;

        public StyleInfo(FillPatternType mode, short backcolor, HorizontalAlignment horizontalAlignment,
                VerticalAlignment verticalAlignment, short rotation, HSSFFont font, BoxStyle box, boolean wrapText,
                boolean cellLocked, boolean cellHidden, boolean shrinkToFit) {
            this.mode = mode;
            this.backcolor = backcolor;
            this.horizontalAlignment = horizontalAlignment;
            this.verticalAlignment = verticalAlignment;
            this.rotation = rotation;
            this.font = font;

            this.box = box;
            this.lcWrapText = shrinkToFit ? false : wrapText;
            this.lcCellLocked = cellLocked;
            this.lcCellHidden = cellHidden;
            this.lcShrinkToFit = shrinkToFit;
            hashCode = computeHash();
        }

        protected int computeHash() {
            int hash = mode.hashCode();
            hash = 31 * hash + backcolor;
            hash = 31 * hash + horizontalAlignment.hashCode();
            hash = 31 * hash + verticalAlignment.hashCode();
            hash = 31 * hash + rotation;
            hash = 31 * hash + (font == null ? 0 : font.getIndex());
            hash = 31 * hash + (box == null ? 0 : box.hashCode());
            hash = 31 * hash + lcDataFormat;
            hash = 31 * hash + (lcWrapText ? 0 : 1);
            hash = 31 * hash + (lcCellLocked ? 0 : 1);
            hash = 31 * hash + (lcCellHidden ? 0 : 1);
            hash = 31 * hash + (lcShrinkToFit ? 0 : 1);
            return hash;
        }

        public void setDataFormat(short dataFormat) {
            this.lcDataFormat = dataFormat;
            hashCode = computeHash();
        }

        public boolean hasDataFormat() {
            return lcDataFormat != -1;
        }

        public short getDataFormat() {
            return lcDataFormat;
        }

        @Override
        public int hashCode() {
            return hashCode;
        }

        @Override
        public boolean equals(Object o) {
            StyleInfo s = (StyleInfo) o;

            return s.mode == mode && s.backcolor == backcolor && s.horizontalAlignment == horizontalAlignment
                    && s.verticalAlignment == verticalAlignment && s.rotation == rotation
                    && (s.font == null ? font == null : (font != null && s.font.getIndex() == font.getIndex()))
                    && (s.box == null ? box == null : (box != null && s.box.equals(box))) && s.rotation == rotation
                    && s.lcWrapText == lcWrapText && s.lcCellLocked == lcCellLocked
                    && s.lcCellHidden == lcCellHidden && s.lcShrinkToFit == lcShrinkToFit; //FIXME should dataformat be part of equals? it is part of toString()...
        }

        @Override
        public String toString() {
            return "(" + mode + "," + backcolor + "," + horizontalAlignment + "," + verticalAlignment + ","
                    + rotation + "," + font + "," + box + "," + lcDataFormat + "," + lcWrapText + "," + lcCellLocked
                    + "," + lcCellHidden + "," + lcShrinkToFit + ")";
        }
    }

    protected class CellSettings {
        private CellType cellType;
        private HSSFCellStyle cellStyle;
        private Object cellValue;
        private String formula;
        private Hyperlink link;

        public CellSettings() {
        }

        public CellSettings(HSSFCellStyle cellStyle) {
            this(CellType.BLANK, cellStyle, null);
        }

        public CellSettings(CellType cellType, HSSFCellStyle cellStyle, Object cellValue) {
            this(cellType, cellStyle, cellValue, null);
        }

        public CellSettings(CellType cellType, HSSFCellStyle cellStyle, Object cellValue, String formula) {
            this(cellType, cellStyle, cellValue, formula, null);
        }

        public CellSettings(CellType cellType, HSSFCellStyle cellStyle, Object cellValue, String formula,
                Hyperlink link) {
            this.cellType = cellType;
            this.cellStyle = cellStyle;
            this.cellValue = cellValue;
            this.formula = formula;
            this.link = link;
        }

        public HSSFCellStyle getCellStyle() {
            return cellStyle;
        }

        public void setCellStyle(HSSFCellStyle cellStyle) {
            this.cellStyle = cellStyle;
        }

        public CellType getCellType() {
            return cellType;
        }

        public void setCellType(CellType cellType) {
            this.cellType = cellType;
        }

        public Object getCellValue() {
            return cellValue;
        }

        public void setCellValue(Object cellValue) {
            this.cellValue = cellValue;
        }

        public String getFormula() {
            return formula;
        }

        public void setFormula(String formula) {
            this.formula = formula;
        }

        public Hyperlink getLink() {
            return link;
        }

        public void setLink(Hyperlink link) {
            this.link = link;
        }

        public void importValues(CellType cellType, HSSFCellStyle cellStyle, Object cellValue) {
            this.importValues(cellType, cellStyle, cellValue, null);
        }

        public void importValues(CellType cellType, HSSFCellStyle cellStyle, Object cellValue, String formula) {
            this.importValues(cellType, cellStyle, cellValue, formula, null);
        }

        public void importValues(CellType cellType, HSSFCellStyle cellStyle, Object cellValue, String formula,
                Hyperlink link) {
            this.cellType = cellType;
            this.cellStyle = cellStyle;
            this.cellValue = cellValue;
            this.formula = formula;
            this.link = link;
        }
    }
}

class ImageSettings {

    private int index;
    private ClientAnchor.AnchorType anchorType;

    public ImageSettings() {
    }

    public ImageSettings(int index, ClientAnchor.AnchorType anchorType) {
        this.index = index;
        this.anchorType = anchorType;
    }

    public int getIndex() {
        return index;
    }

    public ClientAnchor.AnchorType getAnchorType() {
        return anchorType;
    }
}