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

Java tutorial

Introduction

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

Source

/*
 * JasperReports - Free Java Reporting Library.
 * Copyright (C) 2001 - 2014 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.geom.Dimension2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import jxl.CellView;
import jxl.JXLException;
import jxl.Range;
import jxl.SheetSettings;
import jxl.Workbook;
import jxl.WorkbookSettings;
import jxl.biff.DisplayFormat;
import jxl.format.Alignment;
import jxl.format.BoldStyle;
import jxl.format.Border;
import jxl.format.BorderLineStyle;
import jxl.format.Colour;
import jxl.format.Orientation;
import jxl.format.PageOrientation;
import jxl.format.PaperSize;
import jxl.format.Pattern;
import jxl.format.RGB;
import jxl.format.UnderlineStyle;
import jxl.format.VerticalAlignment;
import jxl.read.biff.BiffException;
import jxl.write.Blank;
import jxl.write.DateFormat;
import jxl.write.DateTime;
import jxl.write.Formula;
import jxl.write.Label;
import jxl.write.Number;
import jxl.write.NumberFormat;
import jxl.write.WritableCellFormat;
import jxl.write.WritableFont;
import jxl.write.WritableHyperlink;
import jxl.write.WritableImage;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;
import jxl.write.biff.CellValue;
import jxl.write.biff.RowsExceededException;
import net.sf.jasperreports.engine.DefaultJasperReportsContext;
import net.sf.jasperreports.engine.JRCommonGraphicElement;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRFont;
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.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.JRPrintText;
import net.sf.jasperreports.engine.JRPropertiesUtil;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JRWrappingSvgRenderer;
import net.sf.jasperreports.engine.JasperReportsContext;
import net.sf.jasperreports.engine.Renderable;
import net.sf.jasperreports.engine.RenderableUtil;
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.ooxml.JRXlsxExporter;
import net.sf.jasperreports.engine.export.type.ImageAnchorTypeEnum;
import net.sf.jasperreports.engine.fonts.FontFamily;
import net.sf.jasperreports.engine.fonts.FontInfo;
import net.sf.jasperreports.engine.fonts.FontUtil;
import net.sf.jasperreports.engine.type.HorizontalAlignEnum;
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.RenderableTypeEnum;
import net.sf.jasperreports.engine.type.RotationEnum;
import net.sf.jasperreports.engine.type.VerticalAlignEnum;
import net.sf.jasperreports.engine.util.JRImageLoader;
import net.sf.jasperreports.engine.util.JRStyledText;
import net.sf.jasperreports.export.JxlExporterConfiguration;
import net.sf.jasperreports.export.JxlReportConfiguration;
import net.sf.jasperreports.export.XlsExporterConfiguration;
import net.sf.jasperreports.export.XlsReportConfiguration;
import net.sf.jasperreports.repo.RepositoryUtil;

import org.apache.commons.collections.map.ReferenceMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @deprecated To be removed. Use {@link JRXlsExporter} or {@link JRXlsxExporter} instead.
 * @author Manuel Paul (mpaul@ratundtat.com)
 * @version $Id: JExcelApiExporter.java 7366 2014-11-07 01:25:15Z teodord $
 */
public class JExcelApiExporter
        extends JRXlsAbstractExporter<JxlReportConfiguration, JxlExporterConfiguration, JExcelApiExporterContext> {

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

    /**
     * @deprecated Replaced by {@link JxlExporterConfiguration#PROPERTY_USE_TEMP_FILE}.
     */
    public static final String PROPERTY_USE_TEMP_FILE = JxlExporterConfiguration.PROPERTY_USE_TEMP_FILE;

    /**
     * @deprecated Replaced by {@link JxlReportConfiguration#PROPERTY_COMPLEX_FORMAT}.
     */
    public static final String PROPERTY_COMPLEX_FORMAT = JxlReportConfiguration.PROPERTY_COMPLEX_FORMAT;

    /**
     * The exporter key, as used in
     * {@link GenericElementHandlerEnviroment#getHandler(net.sf.jasperreports.engine.JRGenericElementType, String)}.
     */
    public static final String JXL_EXPORTER_KEY = JRPropertiesUtil.PROPERTY_PREFIX + "jxl";

    protected static final Colour WHITE = Colour.WHITE;
    protected static final Colour BLACK = Colour.BLACK;

    protected static final String EMPTY_SHEET_NAME = "Sheet1";

    private static Map<Color, Colour> colorsCache = new ReferenceMap();

    private static final Colour[] FIXED_COLOURS = new Colour[] { WHITE, BLACK, Colour.PALETTE_BLACK,
            Colour.DEFAULT_BACKGROUND, Colour.DEFAULT_BACKGROUND1, Colour.AUTOMATIC, Colour.UNKNOWN };

    private Map<StyleInfo, WritableCellFormat> loadedCellStyles = new HashMap<StyleInfo, WritableCellFormat>();

    private WritableWorkbook workbook;

    private WritableSheet sheet;

    private Pattern backgroundMode = Pattern.SOLID;

    private Map<String, NumberFormat> numberFormats = new HashMap<String, NumberFormat>();
    private Map<String, DateFormat> dateFormats = new HashMap<String, DateFormat>();

    protected Map<Color, Colour> workbookColours = new HashMap<Color, Colour>();
    protected Map<Colour, RGB> usedColours = new HashMap<Colour, RGB>();

    protected Map<String, List<JExcelApiLocalHyperlinkInfo>> anchorLinks = new HashMap<String, List<JExcelApiLocalHyperlinkInfo>>();
    protected Map<Integer, List<JExcelApiLocalHyperlinkInfo>> pageLinks = new HashMap<Integer, List<JExcelApiLocalHyperlinkInfo>>();

    protected class ExporterContext extends BaseExporterContext implements JExcelApiExporterContext {
    }

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

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

        exporterContext = new ExporterContext();
    }

    /**
     *
     */
    protected Class<JxlExporterConfiguration> getConfigurationInterface() {
        return JxlExporterConfiguration.class;
    }

    /**
     *
     */
    protected Class<JxlReportConfiguration> getItemConfigurationInterface() {
        return JxlReportConfiguration.class;
    }

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

        XlsExporterConfiguration configuration = getCurrentConfiguration();

        if (configuration.isCreateCustomPalette()) {
            initCustomPalette();
        }
    }

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

        XlsReportConfiguration configuration = getCurrentItemConfiguration();

        if (configuration.isWhitePageBackground()) {
            this.backgroundMode = Pattern.SOLID;
        } else {
            this.backgroundMode = Pattern.NONE;
        }

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

    protected void initCustomPalette() {
        //mark "fixed" colours as always used
        for (int i = 0; i < FIXED_COLOURS.length; i++) {
            Colour colour = FIXED_COLOURS[i];
            setColourUsed(colour);
        }
    }

    protected void setColourUsed(Colour colour) {
        usedColours.put(colour, colour.getDefaultRGB());
    }

    protected void setColourUsed(Colour colour, Color reportColour) {
        if (log.isDebugEnabled()) {
            log.debug("Modifying palette colour " + colour.getValue() + " to " + reportColour);
        }

        int red = reportColour.getRed();
        int green = reportColour.getGreen();
        int blue = reportColour.getBlue();

        workbook.setColourRGB(colour, red, green, blue);
        RGB customRGB = new RGB(red, green, blue);
        usedColours.put(colour, customRGB);
    }

    protected void openWorkbook(OutputStream os) throws JRException {
        JxlExporterConfiguration configuration = getCurrentConfiguration();

        WorkbookSettings settings = new WorkbookSettings();
        settings.setUseTemporaryFileDuringWrite(configuration.isUseTempFile());

        InputStream templateIs = null;

        try {
            String lcWorkbookTemplate = workbookTemplate == null ? configuration.getWorkbookTemplate()
                    : workbookTemplate;
            if (lcWorkbookTemplate == null) {
                workbook = Workbook.createWorkbook(os, settings);
            } else {
                templateIs = RepositoryUtil.getInstance(jasperReportsContext)
                        .getInputStreamFromLocation(lcWorkbookTemplate);
                if (templateIs == null) {
                    throw new JRRuntimeException("Workbook template not found at : " + lcWorkbookTemplate);
                } else {
                    Workbook template = Workbook.getWorkbook(templateIs);
                    workbook = Workbook.createWorkbook(os, template, settings);
                    boolean keepSheets = keepTemplateSheets == null ? configuration.isKeepWorkbookTemplateSheets()
                            : keepTemplateSheets;
                    if (!keepSheets) {
                        for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
                            workbook.removeSheet(i);
                        }
                    } else {
                        sheetIndex += workbook.getNumberOfSheets();
                    }
                }
            }

            firstPageNotSet = true;
            anchorLinks.clear();
            pageLinks.clear();
        } catch (IOException e) {
            throw new JRException("Error generating XLS report : " + jasperPrint.getName(), e);
        } catch (BiffException e) {
            throw new JRException("Error generating XLS report : " + jasperPrint.getName(), e);
        } finally {
            if (templateIs != null) {
                try {
                    templateIs.close();
                } catch (IOException e) {
                }
            }
        }
    }

    protected void createSheet(CutsInfo xCuts, SheetInfo sheetInfo) {
        sheet = workbook.createSheet(sheetInfo.sheetName, Integer.MAX_VALUE);
        setSheetSettings(sheetInfo, sheet);
    }

    protected void closeWorkbook(OutputStream os) throws JRException {
        if (sheet == null)//empty document
        {
            //creating an empty sheet so that write() doesn't fail
            workbook.createSheet(EMPTY_SHEET_NAME, Integer.MAX_VALUE);
        }

        Range[] range = null;
        List<JExcelApiLocalHyperlinkInfo> hyperlinkInfoList = null;

        for (String href : anchorLinks.keySet()) { // the anchorLinks map contains no entries for reports with ignore anchors == true;
            range = workbook.findByName(href);
            hyperlinkInfoList = anchorLinks.get(href);
            if (range != null && hyperlinkInfoList != null) {
                for (JExcelApiLocalHyperlinkInfo hyperlinkInfo : hyperlinkInfoList) {
                    WritableSheet anchorSheet = workbook.getSheet(range[0].getFirstSheetIndex());
                    WritableHyperlink hyperlink = new WritableHyperlink(hyperlinkInfo.getCol(),
                            hyperlinkInfo.getRow(), hyperlinkInfo.getLastCol(), hyperlinkInfo.getLastRow(),
                            hyperlinkInfo.getDescription(), anchorSheet, range[0].getTopLeft().getColumn(),
                            range[0].getTopLeft().getRow(), range[0].getBottomRight().getColumn(),
                            range[0].getBottomRight().getRow());
                    try {
                        hyperlinkInfo.getSheet().addHyperlink(hyperlink);
                    } catch (Exception e) {
                        throw new JRException(e);
                    }
                }
            }
        }

        int index = 0;
        for (Integer linkPage : pageLinks.keySet()) { // the pageLinks map contains no entries for reports with ignore hyperlinks == true;
            hyperlinkInfoList = pageLinks.get(linkPage);
            if (hyperlinkInfoList != null && !hyperlinkInfoList.isEmpty()) {
                WritableSheet anchorSheet = null;
                for (JExcelApiLocalHyperlinkInfo hyperlinkInfo : hyperlinkInfoList) {
                    index = onePagePerSheetMap.get(linkPage - 1) != null
                            ? (onePagePerSheetMap.get(linkPage - 1) ? Math.max(0, linkPage - 1)
                                    : Math.max(0, sheetsBeforeCurrentReportMap.get(linkPage)))
                            : 0;
                    anchorSheet = workbook.getSheet(index);
                    WritableHyperlink hyperlink = new WritableHyperlink(hyperlinkInfo.getCol(),
                            hyperlinkInfo.getRow(), hyperlinkInfo.getLastCol(), hyperlinkInfo.getLastRow(),
                            hyperlinkInfo.getDescription(), anchorSheet, 0, 0, 0, 0);
                    try {
                        hyperlinkInfo.getSheet().addHyperlink(hyperlink);
                    } catch (Exception e) {
                        throw new JRException(e);
                    }
                }
            }
        }

        try {
            workbook.write();
            workbook.close();
        } catch (IOException e) {
            throw new JRException("Error generating XLS report : " + jasperPrint.getName(), e);
        } catch (WriteException e) {
            throw new JRException("Error generating XLS report : " + jasperPrint.getName(), e);
        }
    }

    protected void setColumnWidth(int col, int width, boolean autoFit) {
        CellView cv = new CellView();
        if (autoFit) {
            cv.setAutosize(true);
        } else {
            cv.setSize(43 * width);
        }
        sheet.setColumnView(col, cv);
    }

    protected void setRowHeight(int rowIndex, int lastRowHeight, Cut yCut, XlsRowLevelInfo levelInfo)
            throws JRException {
        boolean isAutoFit = yCut.hasProperty(JRXlsAbstractExporter.PROPERTY_AUTO_FIT_ROW)
                && (Boolean) yCut.getProperty(JRXlsAbstractExporter.PROPERTY_AUTO_FIT_ROW);
        if (!isAutoFit) {
            try {
                sheet.setRowView(rowIndex, LengthUtil.twip(lastRowHeight));
            } catch (RowsExceededException e) {
                throw new JRException("Error generating XLS report : " + jasperPrint.getName(), e);
            }
        }
    }

    //   protected void setCell(JRExporterGridCell gridCell, int x, int y)
    //   {
    //   }

    protected void addBlankCell(JRExporterGridCell gridCell, int colIndex, int rowIndex) throws JRException {
        Colour forecolor = BLACK;
        if (gridCell.getForecolor() != null) {
            forecolor = getWorkbookColour(gridCell.getForecolor());
        }

        Pattern mode = backgroundMode;
        Colour backcolor = WHITE;

        JxlReportConfiguration configuration = getCurrentItemConfiguration();

        if (!configuration.isIgnoreCellBackground() && gridCell.getCellBackcolor() != null) {
            mode = Pattern.SOLID;
            backcolor = getWorkbookColour(gridCell.getCellBackcolor(), true);
        }

        WritableFont cellFont = getLoadedFont(getDefaultFont(), forecolor.getValue(), getLocale());
        WritableCellFormat cellStyle = getLoadedCellStyle(mode, backcolor, cellFont, gridCell);

        try {
            sheet.addCell(new Blank(colIndex, rowIndex, cellStyle));
        } catch (RowsExceededException e) {
            throw new JRException("Error generating XLS report : " + jasperPrint.getName(), e);//FIXMENOW raise same exception everywhere
        } catch (WriteException e) {
            throw new JRException("Error generating XLS report : " + jasperPrint.getName(), e);
        }
    }

    protected void addOccupiedCell(OccupiedGridCell occupiedGridCell, int colIndex, int rowIndex)
            throws JRException {
    }

    protected void exportLine(JRPrintLine line, JRExporterGridCell gridCell, int col, int row) throws JRException {
        addMergeRegion(gridCell, col, row);

        Colour forecolor2 = getWorkbookColour(line.getLinePen().getLineColor());
        WritableFont cellFont2 = getLoadedFont(getDefaultFont(), forecolor2.getValue(), getLocale());

        Colour backcolor = WHITE;
        Pattern mode = this.backgroundMode;

        JxlReportConfiguration configuration = getCurrentItemConfiguration();

        if (!configuration.isIgnoreCellBackground() && gridCell.getCellBackcolor() != null) {
            mode = Pattern.SOLID;
            backcolor = getWorkbookColour(gridCell.getCellBackcolor(), true);
        }

        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());

        WritableCellFormat cellStyle2 = getLoadedCellStyle(mode, backcolor, cellFont2, boxStyle,
                isCellLocked(line));
        Blank cell2 = new Blank(col, row, cellStyle2);

        try {
            sheet.addCell(cell2);
        } catch (Exception e) {
            throw new JRException("Can't add cell.", e);
        }
    }

    protected void exportRectangle(JRPrintGraphicElement element, JRExporterGridCell gridCell, int col, int row)
            throws JRException {
        addMergeRegion(gridCell, col, row);

        Colour backcolor = WHITE;
        Pattern mode = this.backgroundMode;

        JxlReportConfiguration configuration = getCurrentItemConfiguration();

        if (!configuration.isIgnoreCellBackground() && gridCell.getCellBackcolor() != null) {
            mode = Pattern.SOLID;
            backcolor = getWorkbookColour(gridCell.getCellBackcolor(), true);
        }

        Colour forecolor = getWorkbookColour(element.getLinePen().getLineColor());
        WritableFont cellFont2 = getLoadedFont(getDefaultFont(), forecolor.getValue(), getLocale());
        WritableCellFormat cellStyle2 = getLoadedCellStyle(mode, backcolor, cellFont2, gridCell,
                isCellLocked(element));

        Blank cell2 = new Blank(col, row, cellStyle2);

        try {
            sheet.addCell(cell2);
        } catch (Exception e) {
            throw new JRException("Can't add cell.", e);
        }
    }

    public void exportText(JRPrintText text, JRExporterGridCell gridCell, int col, int row) throws JRException {
        addMergeRegion(gridCell, col, row);

        JRStyledText styledText = getStyledText(text);

        if (styledText != null) {
            Colour forecolor = getWorkbookColour(text.getForecolor());
            WritableFont cellFont = this.getLoadedFont(text, forecolor.getValue(), getTextLocale(text));

            TextAlignHolder alignment = getTextAlignHolder(text);
            int horizontalAlignment = getHorizontalAlignment(alignment);
            int verticalAlignment = getVerticalAlignment(alignment);
            int rotation = getRotation(alignment);

            Pattern mode = this.backgroundMode;
            Colour backcolor = WHITE;

            JxlReportConfiguration configuration = getCurrentItemConfiguration();

            if (!configuration.isIgnoreCellBackground() && gridCell.getCellBackcolor() != null) {
                mode = Pattern.SOLID;
                backcolor = getWorkbookColour(gridCell.getCellBackcolor(), true);
            }

            StyleInfo baseStyle = new StyleInfo(mode, backcolor, horizontalAlignment, verticalAlignment, rotation,
                    cellFont, gridCell,
                    isWrapText(text)
                            || Boolean.TRUE.equals(((JExcelApiExporterNature) nature).getColumnAutoFit(text)),
                    isCellLocked(text));

            if (!configuration.isIgnoreAnchors() && text.getAnchorName() != null) {
                int lastCol = Math.max(0, col + gridCell.getColSpan() - 1);
                int lastRow = Math.max(0, row + gridCell.getRowSpan() - 1);
                workbook.addNameArea(text.getAnchorName(), sheet, col, row, lastCol, lastRow);
            }

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

            //test for ignore hyperlink done here
            if (!ignoreHyperlink) {
                exportHyperlink(text, styledText.getText(), gridCell, col, row);
            }

            try {
                addCell(col, row, text, styledText.getText(), baseStyle);
            } catch (Exception e) {
                throw new JRException("Can't add cell.", e);
            }
        }
    }

    public void exportHyperlink(JRPrintHyperlink link, String description, JRExporterGridCell gridCell, int col,
            int row) throws JRException {
        JRHyperlinkProducer customHandler = getHyperlinkProducer(link);
        if (customHandler == null) {
            switch (link.getHyperlinkTypeValue()) {
            case REFERENCE: {
                exportHyperlink(link.getHyperlinkReference(), col, row, col, row);
                break;
            }
            case LOCAL_ANCHOR: {
                // test for ignore anchor done here
                if (!getCurrentItemConfiguration().isIgnoreAnchors()) {
                    String href = link.getHyperlinkAnchor();
                    if (href != null) {
                        int lastCol = Math.max(0, col + gridCell.getColSpan() - 1);
                        int lastRow = Math.max(0, row + gridCell.getRowSpan() - 1);
                        JExcelApiLocalHyperlinkInfo hyperlinkInfo = new JExcelApiLocalHyperlinkInfo(description,
                                sheet, col, row, lastCol, lastRow);
                        if (anchorLinks.containsKey(href)) {
                            anchorLinks.get(href).add(hyperlinkInfo);
                        } else {
                            List<JExcelApiLocalHyperlinkInfo> hyperlinkInfoList = new ArrayList<JExcelApiLocalHyperlinkInfo>();
                            hyperlinkInfoList.add(hyperlinkInfo);
                            anchorLinks.put(href, hyperlinkInfoList);
                        }
                    }
                }
                break;
            }
            case LOCAL_PAGE: {
                // test for ignore anchor done here
                if (!getCurrentItemConfiguration().isIgnoreAnchors()) {
                    Integer href = getCurrentItemConfiguration().isOnePagePerSheet() ? link.getHyperlinkPage() : 0;
                    if (href != null) {
                        int lastCol = Math.max(0, col + gridCell.getColSpan() - 1);
                        int lastRow = Math.max(0, row + gridCell.getRowSpan() - 1);
                        JExcelApiLocalHyperlinkInfo hyperlinkInfo = new JExcelApiLocalHyperlinkInfo(description,
                                sheet, col, row, lastCol, lastRow);
                        if (pageLinks.containsKey(sheetsBeforeCurrentReport + href)) {
                            pageLinks.get(sheetsBeforeCurrentReport + href).add(hyperlinkInfo);
                        } else {
                            List<JExcelApiLocalHyperlinkInfo> hyperlinkInfoList = new ArrayList<JExcelApiLocalHyperlinkInfo>();
                            hyperlinkInfoList.add(hyperlinkInfo);
                            pageLinks.put(sheetsBeforeCurrentReport + href, hyperlinkInfoList);
                        }
                    }
                }
                break;
            }
            case REMOTE_ANCHOR:
            case REMOTE_PAGE:
            case NONE:
            default: {
            }
            }
        } else {
            exportHyperlink(customHandler.getHyperlink(link), col, row, col, row);
        }
    }

    protected void exportHyperlink(String href, int col, int row, int lastCol, int lastRow) throws JRException {
        if (href != null) {
            try {
                URL url = new URL(href);
                WritableHyperlink hyperlink = new WritableHyperlink(col, row, lastCol, lastRow, url);
                sheet.addHyperlink(hyperlink);
            } catch (MalformedURLException e) {
                if (log.isWarnEnabled()) {
                    log.warn("Reference \"" + href + "\" could not be parsed as URL.", e);
                }
            } catch (Exception e) {
                throw new JRException(e);
            }
        }
    }

    protected void addCell(int x, int y, JRPrintText text, String textStr, StyleInfo baseStyle)
            throws WriteException, RowsExceededException, JRException {
        CellValue cellValue = null;

        TextValue textValue = null;

        String textFormula = getFormula(text);
        if (textFormula != null) {
            // if the cell has formula, we try create a formula cell
            textValue = getTextValue(text, textStr);
            cellValue = getFormulaCellValue(x, y, text, textValue, textFormula, baseStyle, isComplexFormat(text));
        }

        if (cellValue == null) {
            JxlReportConfiguration configuration = getCurrentItemConfiguration();

            // there was no formula, or the formula cell creation failed
            if (configuration.isDetectCellType()) {
                if (textFormula == null) {
                    // there was no formula, so textValue was not created
                    textValue = getTextValue(text, textStr);
                }
                cellValue = getDetectedCellValue(x, y, text, textValue, baseStyle, isComplexFormat(text));
            } else {
                cellValue = getLabelCell(x, y, textStr, baseStyle);
            }
        }

        sheet.addCell(cellValue);
    }

    protected CellValue getFormulaCellValue(int x, int y, JRPrintText textElement, TextValue textValue,
            String formula, StyleInfo baseStyle, boolean complexFormat) throws JRException {
        FormulaTextValueHandler handler = new FormulaTextValueHandler(x, y, textElement, formula, baseStyle,
                complexFormat);
        textValue.handle(handler);
        return handler.getResult();
    }

    protected CellValue getDetectedCellValue(int x, int y, JRPrintText textElement, TextValue textValue,
            StyleInfo baseStyle, boolean complexFormat) throws JRException {
        CellTextValueHandler handler = new CellTextValueHandler(x, y, textElement, baseStyle, complexFormat);
        textValue.handle(handler);
        return handler.getResult();
    }

    protected class FormulaTextValueHandler implements TextValueHandler {
        private final int x;
        private final int y;
        private final JRPrintText textElement;
        private final String formula;
        private final StyleInfo baseStyle;
        private final boolean cellComplexFormat;

        private CellValue result;

        //      public FormulaTextValueHandler(int x, int y, String formula, StyleInfo baseStyle)
        //      {
        //         this(x, y, formula, baseStyle, false);
        //      }

        public FormulaTextValueHandler(int x, int y, JRPrintText textElement, String formula, StyleInfo baseStyle,
                boolean cellComplexFormat) {
            this.x = x;
            this.y = y;
            this.textElement = textElement;
            this.formula = formula;
            this.baseStyle = baseStyle;
            this.cellComplexFormat = cellComplexFormat;
        }

        public void handle(StringTextValue textValue) throws JRException {
            result = formula();
        }

        public void handle(NumberTextValue textValue) throws JRException {
            String convertedPattern = getConvertedPattern(textElement, textValue.getPattern());
            if (convertedPattern != null) {
                baseStyle.setDisplayFormat(getNumberFormat(convertedPattern, cellComplexFormat));
            }

            result = formula();
        }

        public void handle(DateTextValue textValue) throws JRException {
            baseStyle.setDisplayFormat(getDateFormat(getConvertedPattern(textElement, textValue.getPattern())));
            result = formula();
        }

        public void handle(BooleanTextValue textValue) throws JRException {
            result = formula();
        }

        protected Formula formula() throws JRException {
            try {
                return new Formula(x, y, formula, getLoadedCellStyle(baseStyle));
            } catch (Exception e)//FIXMENOW what exceptions could we get here?
            {
                if (log.isWarnEnabled()) {
                    log.warn(e.getMessage(), e);
                }
            }
            return null;
        }

        public CellValue getResult() {
            return result;
        }
    }

    protected class CellTextValueHandler implements TextValueHandler {
        private final int x;
        private final int y;
        private final JRPrintText textElement;
        private final StyleInfo baseStyle;
        private final boolean cellComplexFormat;

        private CellValue result;

        //      public CellTextValueHandler(int x, int y, StyleInfo baseStyle)
        //      {
        //         this(x, y, baseStyle, false);
        //      }

        public CellTextValueHandler(int x, int y, JRPrintText textElement, StyleInfo baseStyle,
                boolean cellComplexFormat) {
            this.x = x;
            this.y = y;
            this.textElement = textElement;
            this.baseStyle = baseStyle;
            this.cellComplexFormat = cellComplexFormat;
        }

        public void handle(StringTextValue textValue) throws JRException {
            WritableCellFormat cellStyle = getLoadedCellStyle(baseStyle);
            result = new Label(x, y, textValue.getText(), cellStyle);
        }

        public void handle(NumberTextValue textValue) throws JRException {
            String convertedPattern = getConvertedPattern(textElement, textValue.getPattern());
            if (convertedPattern != null) {
                baseStyle.setDisplayFormat(getNumberFormat(convertedPattern, cellComplexFormat));
            }

            WritableCellFormat cellStyle = getLoadedCellStyle(baseStyle);
            if (textValue.getValue() == null) {
                result = blank(cellStyle);
            } else {
                result = new Number(x, y, textValue.getValue().doubleValue(), cellStyle);
            }
        }

        public void handle(DateTextValue textValue) throws JRException {
            baseStyle.setDisplayFormat(getDateFormat(getConvertedPattern(textElement, textValue.getPattern())));//FIXMEFORMAT why no null test here like in number?
            WritableCellFormat cellStyle = getLoadedCellStyle(baseStyle);
            Date date = textValue.getValue();
            if (date == null) {
                result = blank(cellStyle);
            } else {
                date = translateDateValue(textElement, date);
                result = new DateTime(x, y, date, cellStyle);
            }
        }

        public void handle(BooleanTextValue textValue) throws JRException {
            WritableCellFormat cellStyle = getLoadedCellStyle(baseStyle);
            if (textValue.getValue() == null) {
                result = blank(cellStyle);
            } else {
                result = new jxl.write.Boolean(x, y, textValue.getValue().booleanValue(), cellStyle);
            }
        }

        protected Blank blank(WritableCellFormat cellStyle) {
            return new Blank(x, y, cellStyle);
        }

        public CellValue getResult() {
            return result;
        }

        public boolean isCellComplexFormat() {
            return cellComplexFormat;
        }

    }

    protected NumberFormat getNumberFormat(String convertedPattern, boolean isComplexFormat) {
        NumberFormat cellFormat = numberFormats.get(convertedPattern);
        if (cellFormat == null) {
            if (isComplexFormat) {
                cellFormat = new NumberFormat(convertedPattern, NumberFormat.COMPLEX_FORMAT);
            } else {
                cellFormat = new NumberFormat(convertedPattern);
            }
            numberFormats.put(convertedPattern, cellFormat);
        }
        return cellFormat;
    }

    protected DateFormat getDateFormat(String convertedPattern) {
        DateFormat cellFormat = dateFormats.get(convertedPattern);
        if (cellFormat == null) {
            cellFormat = new DateFormat(convertedPattern);
            dateFormats.put(convertedPattern, cellFormat);
        }
        return cellFormat;
    }

    protected CellValue getLabelCell(int x, int y, String textStr, StyleInfo baseStyle) throws JRException {
        WritableCellFormat cellStyle = getLoadedCellStyle(baseStyle);
        return new Label(x, y, textStr, cellStyle);
    }

    protected void addMergeRegion(JRExporterGridCell gridCell, int x, int y) throws JRException {
        boolean isCollapseRowSpan = getCurrentItemConfiguration().isCollapseRowSpan();

        if (gridCell.getColSpan() > 1 || (gridCell.getRowSpan() > 1 && !isCollapseRowSpan)) {
            try {
                if (isCollapseRowSpan) {
                    sheet.mergeCells(x, y, (x + gridCell.getColSpan() - 1), y);
                } else {
                    sheet.mergeCells(x, y, (x + gridCell.getColSpan() - 1), (y + gridCell.getRowSpan() - 1));
                }
            } catch (JXLException e) {
                throw new JRException("Can't merge cells.", e);
            }
        }
    }

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

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

    private int getRotation(TextAlignHolder alignment) {
        switch (alignment.rotation) {
        case LEFT:
            return Orientation.PLUS_90.getValue();
        case RIGHT:
            return Orientation.MINUS_90.getValue();
        case UPSIDE_DOWN:
        case NONE:
        default:
            return Orientation.HORIZONTAL.getValue();
        }
    }

    public void exportImage(JRPrintImage element, JRExporterGridCell gridCell, int col, int row, int emptyCols,
            int yCutsRow, JRGridLayout layout) throws JRException {
        addMergeRegion(gridCell, col, row);

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

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

        int availableImageHeight = element.getHeight() - topPadding - bottomPadding;
        availableImageHeight = availableImageHeight < 0 ? 0 : availableImageHeight;

        Renderable renderer = element.getRenderable();

        if (renderer != null && availableImageWidth > 0 && availableImageHeight > 0) {
            if (renderer.getTypeValue() == RenderableTypeEnum.IMAGE) {
                // Image renderers are all asked for their image data and dimension at some point. 
                // Better to test and replace the renderer now, in case of lazy load error.
                renderer = RenderableUtil.getInstance(jasperReportsContext).getOnErrorRendererForImageData(renderer,
                        element.getOnErrorTypeValue());
                if (renderer != null) {
                    renderer = RenderableUtil.getInstance(jasperReportsContext)
                            .getOnErrorRendererForDimension(renderer, element.getOnErrorTypeValue());
                }
            } else {
                renderer = new JRWrappingSvgRenderer(renderer,
                        new Dimension(element.getWidth(), element.getHeight()),
                        ModeEnum.OPAQUE == element.getModeValue() ? element.getBackcolor() : null);
            }
        } else {
            renderer = null;
        }

        if (renderer != null) {
            int normalWidth = availableImageWidth;
            int normalHeight = availableImageHeight;

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

            float xalignFactor = 0f;
            switch (element.getHorizontalAlignmentValue()) {
            case RIGHT: {
                xalignFactor = 1f;
                break;
            }
            case CENTER: {
                xalignFactor = 0.5f;
                break;
            }
            case LEFT:
            default: {
                xalignFactor = 0f;
                break;
            }
            }

            float yalignFactor = 0f;
            switch (element.getVerticalAlignmentValue()) {
            case BOTTOM: {
                yalignFactor = 1f;
                break;
            }
            case MIDDLE: {
                yalignFactor = 0.5f;
                break;
            }
            case TOP:
            default: {
                yalignFactor = 0f;
                break;
            }
            }

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

            switch (element.getScaleImageValue()) {
            case CLIP: {
                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();
                grx.scale(scale, scale);
                grx.clip(new Rectangle(0, 0, availableImageWidth, availableImageHeight));

                renderer.render(jasperReportsContext, grx,
                        new Rectangle((int) (xalignFactor * (availableImageWidth - normalWidth)),
                                (int) (yalignFactor * (availableImageHeight - normalHeight)), normalWidth,
                                normalHeight));

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

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

                break;
            }
            case FILL_FRAME: {
                topOffset = topPadding;
                leftOffset = leftPadding;
                bottomOffset = bottomPadding;
                rightOffset = rightPadding;

                imageData = renderer.getImageData(jasperReportsContext);

                break;
            }
            case RETAIN_SHAPE:
            default: {
                if (element.getHeight() > 0) {
                    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;
                    }

                    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));

                    imageData = renderer.getImageData(jasperReportsContext);
                }

                break;
            }
            }

            Pattern mode = this.backgroundMode;
            Colour background = WHITE;

            JxlReportConfiguration configuration = getCurrentItemConfiguration();

            if (!configuration.isIgnoreCellBackground() && gridCell.getCellBackcolor() != null) {
                mode = Pattern.SOLID;
                background = getWorkbookColour(gridCell.getCellBackcolor(), true);
            }

            if (element.getModeValue() == ModeEnum.OPAQUE) {
                background = getWorkbookColour(element.getBackcolor(), true);
            }

            Colour forecolor = getWorkbookColour(element.getLineBox().getPen().getLineColor());

            WritableFont cellFont2 = this.getLoadedFont(getDefaultFont(), forecolor.getValue(), getLocale());

            WritableCellFormat cellStyle2 = getLoadedCellStyle(mode, background, cellFont2, gridCell,
                    isCellLocked(element));

            if (!configuration.isIgnoreAnchors() && element.getAnchorName() != null) {
                int lastCol = Math.max(0, col + gridCell.getColSpan() - 1);
                int lastRow = Math.max(0, row + gridCell.getRowSpan() - 1);
                workbook.addNameArea(element.getAnchorName(), sheet, col, row, lastCol, lastRow);
            }

            Boolean ignoreHyperlink = HyperlinkUtil
                    .getIgnoreHyperlink(XlsReportConfiguration.PROPERTY_IGNORE_HYPERLINK, element);
            if (ignoreHyperlink == null) {
                ignoreHyperlink = configuration.isIgnoreHyperlink();
            }
            if (!ignoreHyperlink) {
                exportHyperlink(element, "", gridCell, col, row);
            }

            try {
                sheet.addCell(new Blank(col, row, cellStyle2));
                double leftPos = getColumnRelativePosition(layout, col, leftOffset);
                double topPos = getRowRelativePosition(layout, yCutsRow, topOffset);
                double rightPos = getColumnRelativePosition(layout, col, element.getWidth() - rightOffset);
                double bottomPos = getRowRelativePosition(layout, yCutsRow, element.getHeight() - bottomOffset);
                WritableImage image = new WritableImage(col + leftPos, row + topPos, rightPos - leftPos,
                        bottomPos - topPos, imageData);

                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;
                    }
                }
                setAnchorType(image, imageAnchorType);
                sheet.addImage(image);
            } catch (Exception ex) {
                throw new JRException("The cell cannot be added", ex);
            } catch (Error err) {
                throw new JRException("The cell cannot be added", err);
            }
        }
    }

    /**
     *
     */
    protected double getColumnRelativePosition(JRGridLayout layout, int col, int offset) {
        double colRelPos = 0;

        int cumulativeColWidth = 0;
        int colIndex = 0;
        while (cumulativeColWidth < offset) {
            int colWidth = sheet.getColumnView(col + colIndex).getSize() / 43;
            if (cumulativeColWidth + colWidth < offset) {
                colIndex++;
            } else {
                colRelPos += colIndex + ((offset - cumulativeColWidth) / (double) colWidth);
            }
            cumulativeColWidth += colWidth;
        }

        return colRelPos;
    }

    /**
     *
     */
    protected double getRowRelativePosition(JRGridLayout layout, int row, int offset) {
        boolean isCollapseRowSpan = getCurrentItemConfiguration().isCollapseRowSpan();

        double rowRelPos = 0;

        //isCollapseRowSpan
        int cumulativeRowHeight = 0;
        int rowIndex = 0;
        while (cumulativeRowHeight < offset) {
            int rowHeight = isCollapseRowSpan ? layout.getMaxRowHeight(row + rowIndex)
                    : layout.getRowHeight(row + rowIndex);
            if (cumulativeRowHeight + rowHeight < offset) {
                rowIndex++;
            } else {
                rowRelPos += rowIndex + ((offset - cumulativeRowHeight) / (double) rowHeight);
            }
            cumulativeRowHeight += rowHeight;
        }

        return rowRelPos;
    }

    protected Colour getWorkbookColour(Color awtColor, boolean isBackcolor) {
        if (isBackcolor && awtColor.getRGB() == Color.black.getRGB()) {
            return Colour.PALETTE_BLACK;
        }
        return getWorkbookColour(awtColor);
    }

    protected Colour getWorkbookColour(Color awtColor) {
        JxlExporterConfiguration configuration = getCurrentConfiguration();

        Colour colour;
        if (configuration.isCreateCustomPalette()) {
            colour = workbookColours.get(awtColor);
            if (colour == null) {
                colour = determineWorkbookColour(awtColor);
                workbookColours.put(awtColor, colour);
            }
        } else {
            colour = getNearestColour(awtColor);
        }
        return colour;
    }

    protected Colour determineWorkbookColour(Color awtColor) {
        //nearest match
        int minDist = 999;
        Colour minColour = null;

        //nearest match among the available (not used) colours
        int minDistAvailable = 999;
        Colour minColourAvailable = null;

        Colour[] colors = Colour.getAllColours();
        for (int i = 0; i < colors.length; i++) {
            Colour colour = colors[i];
            RGB customRGB = usedColours.get(colour);

            RGB rgb = customRGB == null ? colour.getDefaultRGB() : customRGB;
            int dist = rgbDistance(awtColor, rgb);
            if (dist < minDist) {
                minDist = dist;
                minColour = colour;
            }

            if (dist == 0)//exact match
            {
                break;
            }

            if (customRGB == null)//the colour is not used
            {
                if (dist < minDistAvailable) {
                    minDistAvailable = dist;
                    minColourAvailable = colour;
                }
            }
        }

        Colour workbookColour;
        if (minDist == 0)//exact match found
        {
            if (!usedColours.containsKey(minColour)) {
                //if the colour is not marked as used, mark it
                setColourUsed(minColour);
            }
            workbookColour = minColour;
        } else if (minColourAvailable == null)//all the colours are used
        {
            if (log.isWarnEnabled()) {
                log.warn("No more available colours in the palette.  Using the nearest match for " + awtColor);
            }
            workbookColour = minColour;
        } else {
            //modifying the nearest available colour to the report colour
            setColourUsed(minColourAvailable, awtColor);
            workbookColour = minColourAvailable;
        }
        return workbookColour;
    }

    protected static Colour getNearestColour(Color awtColor) {
        Colour color = colorsCache.get(awtColor);

        if (color == null) {
            Colour[] colors = Colour.getAllColours();
            if ((colors != null) && (colors.length > 0)) {
                int minDiff = 999;

                for (int i = 0; i < colors.length; i++) {
                    Colour crtColor = colors[i];
                    int diff = rgbDistance(awtColor, crtColor.getDefaultRGB());

                    if (diff < minDiff) {
                        minDiff = diff;
                        color = crtColor;
                    }
                }
            }

            colorsCache.put(awtColor, color);
        }

        return color;
    }

    protected static int rgbDistance(Color awtColor, RGB rgb) {
        return Math.abs(rgb.getRed() - awtColor.getRed()) + Math.abs(rgb.getGreen() - awtColor.getGreen())
                + Math.abs(rgb.getBlue() - awtColor.getBlue());
    }

    /*private static Colour getNearestColour(Color awtColor) {
       Colour retVal = null;
       Colour[] colors = Colour.getAllColours();
        
       int diff = 50;
        
       if (colors != null && colors.length > 0 ){
     Colour crtColor = null;
     for (int i = 0; i < colors.length; i++) {
        crtColor = colors[i];
        
        int red = crtColor.getDefaultRGB().getRed();
        if (Math.abs(awtColor.getRed() - red) < diff) {
           int green = crtColor.getDefaultRGB().getGreen();
           if (Math.abs(awtColor.getGreen() - green) < diff) {
              int blue = crtColor.getDefaultRGB().getBlue();
              if (Math.abs(awtColor.getBlue() - blue) < diff) {
                 retVal = crtColor;
              }
           }
        }
     }
       }
        
       return retVal;
    }*/

    private WritableFont getLoadedFont(JRFont font, int forecolor, Locale locale) throws JRException {
        boolean isFontSizeFixEnabled = getCurrentItemConfiguration().isFontSizeFixEnabled();

        WritableFont cellFont = null;

        if (this.loadedFonts != null && this.loadedFonts.size() > 0) {
            for (int i = 0; i < this.loadedFonts.size(); i++) {
                WritableFont cf = (WritableFont) this.loadedFonts.get(i);

                int fontSize = (int) font.getFontsize();
                if (isFontSizeFixEnabled) {
                    fontSize -= 1;
                }

                String fontName = font.getFontName();

                FontInfo fontInfo = FontUtil.getInstance(jasperReportsContext).getFontInfo(fontName, locale);
                if (fontInfo != null) {
                    //fontName found in font extensions
                    FontFamily family = fontInfo.getFontFamily();
                    String exportFont = family.getExportFont(getExporterKey());
                    if (exportFont != null) {
                        fontName = exportFont;
                    }
                }

                if ((cf.getName().equals(fontName)) && (cf.getColour().getValue() == forecolor)
                        && (cf.getPointSize() == fontSize)
                        && (cf.getUnderlineStyle() == UnderlineStyle.SINGLE ? (font.isUnderline())
                                : (!font.isUnderline()))
                        && (cf.isStruckout() == font.isStrikeThrough())
                        && (cf.getBoldWeight() == BoldStyle.BOLD.getValue() ? (font.isBold()) : (!font.isBold()))
                        && (cf.isItalic() == font.isItalic())) {
                    cellFont = cf;
                    break;
                }
            }
        }

        try {
            if (cellFont == null) {
                int fontSize = (int) font.getFontsize();
                if (isFontSizeFixEnabled) {
                    fontSize -= 1;
                }

                String fontName = font.getFontName();

                cellFont = new WritableFont(WritableFont.createFont(fontName), fontSize,
                        font.isBold() ? WritableFont.BOLD : WritableFont.NO_BOLD, font.isItalic(),
                        font.isUnderline() ? UnderlineStyle.SINGLE : UnderlineStyle.NO_UNDERLINE,
                        Colour.getInternalColour(forecolor));
                cellFont.setStruckout(font.isStrikeThrough());

                this.loadedFonts.add(cellFont);
            }
        } catch (Exception e) {
            throw new JRException("Can't get loaded fonts.", e);
        }

        return cellFont;
    }

    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 BorderLineStyle[] borderStyle = new BorderLineStyle[] { BorderLineStyle.NONE,
                BorderLineStyle.NONE, BorderLineStyle.NONE, BorderLineStyle.NONE };
        protected Colour[] borderColour = new Colour[] { BLACK, BLACK, BLACK, BLACK };
        private int hash;

        public BoxStyle(int side, JRPen pen) {
            borderStyle[side] = getBorderLineStyle(pen);
            borderColour[side] = getWorkbookColour(pen.getLineColor());

            hash = computeHash();
        }

        public BoxStyle(JRExporterGridCell gridCell) {
            JRLineBox lineBox = gridCell.getBox();
            if (lineBox != null) {
                setBox(lineBox);
            }
            JRPrintElement element = gridCell.getElement();
            if (element instanceof JRCommonGraphicElement) {
                setPen(((JRCommonGraphicElement) element).getLinePen());
            }
            hash = computeHash();
        }

        public void setBox(JRLineBox box) {
            borderStyle[TOP] = getBorderLineStyle(box.getTopPen());
            borderColour[TOP] = getWorkbookColour(box.getTopPen().getLineColor());

            borderStyle[BOTTOM] = getBorderLineStyle(box.getBottomPen());
            borderColour[BOTTOM] = getWorkbookColour(box.getBottomPen().getLineColor());

            borderStyle[LEFT] = getBorderLineStyle(box.getLeftPen());
            borderColour[LEFT] = getWorkbookColour(box.getLeftPen().getLineColor());

            borderStyle[RIGHT] = getBorderLineStyle(box.getRightPen());
            borderColour[RIGHT] = getWorkbookColour(box.getRightPen().getLineColor());

            hash = computeHash();
        }

        public void setPen(JRPen pen) {
            if (borderStyle[TOP] == BorderLineStyle.NONE && borderStyle[LEFT] == BorderLineStyle.NONE
                    && borderStyle[BOTTOM] == BorderLineStyle.NONE && borderStyle[RIGHT] == BorderLineStyle.NONE) {
                BorderLineStyle style = getBorderLineStyle(pen);
                Colour colour = getWorkbookColour(pen.getLineColor());

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

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

            hash = computeHash();
        }

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

        public int hashCode() {
            return hash;
        }

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

            return b.borderStyle[TOP].equals(borderStyle[TOP]) && b.borderColour[TOP].equals(borderColour[TOP])
                    && b.borderStyle[LEFT].equals(borderStyle[LEFT])
                    && b.borderColour[LEFT].equals(borderColour[LEFT])
                    && b.borderStyle[BOTTOM].equals(borderStyle[BOTTOM])
                    && b.borderColour[BOTTOM].equals(borderColour[BOTTOM])
                    && b.borderStyle[RIGHT].equals(borderStyle[RIGHT])
                    && b.borderColour[RIGHT].equals(borderColour[RIGHT]);
        }

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

    protected class StyleInfo {
        protected final Pattern mode;
        protected final Colour backcolor;
        protected final int horizontalAlignment;
        protected final int verticalAlignment;
        protected final int rotation;
        protected final WritableFont font;
        protected final BoxStyle box;
        protected final boolean isWrapText;
        protected final boolean isCellLocked;
        private DisplayFormat displayFormat;
        private int hashCode;

        protected StyleInfo(Pattern mode, Colour backcolor, int horizontalAlignment, int verticalAlignment,
                int rotation, WritableFont font, JRExporterGridCell gridCell) {
            this(mode, backcolor, horizontalAlignment, verticalAlignment, rotation, font, new BoxStyle(gridCell),
                    true);
        }

        protected StyleInfo(Pattern mode, Colour backcolor, int horizontalAlignment, int verticalAlignment,
                int rotation, WritableFont font, JRExporterGridCell gridCell, boolean wrapText) {
            this(mode, backcolor, horizontalAlignment, verticalAlignment, rotation, font, new BoxStyle(gridCell),
                    wrapText);
        }

        protected StyleInfo(Pattern mode, Colour backcolor, int horizontalAlignment, int verticalAlignment,
                int rotation, WritableFont font, JRExporterGridCell gridCell, boolean wrapText,
                boolean cellLocked) {
            this(mode, backcolor, horizontalAlignment, verticalAlignment, rotation, font, new BoxStyle(gridCell),
                    wrapText, cellLocked);
        }

        protected StyleInfo(Pattern mode, Colour backcolor, int horizontalAlignment, int verticalAlignment,
                int rotation, WritableFont font, BoxStyle box) {
            this(mode, backcolor, horizontalAlignment, verticalAlignment, rotation, font, box, true);
        }

        protected StyleInfo(Pattern mode, Colour backcolor, int horizontalAlignment, int verticalAlignment,
                int rotation, WritableFont font, BoxStyle box, boolean wrapText) {
            this(mode, backcolor, horizontalAlignment, verticalAlignment, rotation, font, box, wrapText, true);
        }

        protected StyleInfo(Pattern mode, Colour backcolor, int horizontalAlignment, int verticalAlignment,
                int rotation, WritableFont font, BoxStyle box, boolean wrapText, boolean cellLocked) {
            this.mode = mode;
            this.backcolor = backcolor;
            this.horizontalAlignment = horizontalAlignment;
            this.verticalAlignment = verticalAlignment;
            this.rotation = rotation;
            this.font = font;

            this.box = box;
            this.isWrapText = wrapText;
            this.isCellLocked = cellLocked;

            computeHash();
        }

        protected void computeHash() {
            int hash = this.mode.hashCode();
            hash = 31 * hash + this.backcolor.hashCode();
            hash = 31 * hash + this.horizontalAlignment;
            hash = 31 * hash + this.verticalAlignment;
            hash = 31 * hash + this.rotation;
            hash = 31 * hash + this.font.hashCode();
            hash = 31 * hash + (this.box == null ? 0 : this.box.hashCode());
            hash = 31 * hash + (this.displayFormat == null ? 0 : this.displayFormat.hashCode());
            hash = 31 * hash + (this.isWrapText ? 0 : 1);
            hash = 31 * hash + (this.isCellLocked ? 0 : 1);

            hashCode = hash;
        }

        public int hashCode() {
            return hashCode;
        }

        public boolean equals(Object o) {
            StyleInfo k = (StyleInfo) o;

            return k.mode.equals(mode) && k.backcolor.equals(backcolor)
                    && k.horizontalAlignment == horizontalAlignment && k.verticalAlignment == verticalAlignment
                    && k.rotation == rotation && k.font.equals(font)
                    && (k.box == null ? box == null : (box != null && k.box.equals(box)))
                    && (k.displayFormat == null ? displayFormat == null
                            : (displayFormat != null && k.displayFormat.equals(displayFormat))
                                    && k.isWrapText == isWrapText && k.isCellLocked == isCellLocked);
        }

        public DisplayFormat getDisplayFormat() {
            return displayFormat;
        }

        public void setDisplayFormat(DisplayFormat displayFormat) {
            this.displayFormat = displayFormat;
            computeHash();
        }

        public String toString() {
            return "(" + mode + "," + backcolor + "," + horizontalAlignment + "," + verticalAlignment + ","
                    + rotation + "," + font + "," + box + "," + displayFormat + "," + isWrapText + ","
                    + isCellLocked + ")";
        }
    }

    private WritableCellFormat getLoadedCellStyle(Pattern mode, Colour backcolor, WritableFont font,
            JRExporterGridCell gridCell) throws JRException {
        StyleInfo styleKey = new StyleInfo(mode, backcolor, Alignment.LEFT.getValue(),
                VerticalAlignment.TOP.getValue(), Orientation.HORIZONTAL.getValue(), font, gridCell);
        return getLoadedCellStyle(styleKey);
    }

    private WritableCellFormat getLoadedCellStyle(Pattern mode, Colour backcolor, WritableFont font,
            JRExporterGridCell gridCell, boolean cellLocked) throws JRException {
        StyleInfo styleKey = new StyleInfo(mode, backcolor, Alignment.LEFT.getValue(),
                VerticalAlignment.TOP.getValue(), Orientation.HORIZONTAL.getValue(), font, gridCell, true,
                cellLocked);
        return getLoadedCellStyle(styleKey);
    }

    private WritableCellFormat getLoadedCellStyle(Pattern mode, Colour backcolor, WritableFont font, BoxStyle box,
            boolean cellLocked) throws JRException {
        StyleInfo styleKey = new StyleInfo(mode, backcolor, Alignment.LEFT.getValue(),
                VerticalAlignment.TOP.getValue(), Orientation.HORIZONTAL.getValue(), font, box, true, cellLocked);
        return getLoadedCellStyle(styleKey);
    }

    protected WritableCellFormat getLoadedCellStyle(StyleInfo styleKey) throws JRException {
        WritableCellFormat cellStyle = loadedCellStyles.get(styleKey);

        if (cellStyle == null) {
            try {
                if (styleKey.getDisplayFormat() == null) {
                    cellStyle = new WritableCellFormat(styleKey.font);
                } else {
                    cellStyle = new WritableCellFormat(styleKey.font, styleKey.getDisplayFormat());
                }

                cellStyle.setBackground(styleKey.backcolor, styleKey.mode);
                cellStyle.setAlignment(Alignment.getAlignment(styleKey.horizontalAlignment));
                cellStyle.setVerticalAlignment(VerticalAlignment.getAlignment(styleKey.verticalAlignment));
                cellStyle.setOrientation(Orientation.getOrientation(styleKey.rotation));
                cellStyle.setWrap(styleKey.isWrapText);
                cellStyle.setLocked(styleKey.isCellLocked);

                JxlReportConfiguration configuration = getCurrentItemConfiguration();

                if (!configuration.isIgnoreCellBorder()) {
                    BoxStyle box = styleKey.box;
                    cellStyle.setBorder(Border.TOP, box.borderStyle[BoxStyle.TOP], box.borderColour[BoxStyle.TOP]);
                    cellStyle.setBorder(Border.BOTTOM, box.borderStyle[BoxStyle.BOTTOM],
                            box.borderColour[BoxStyle.BOTTOM]);
                    cellStyle.setBorder(Border.LEFT, box.borderStyle[BoxStyle.LEFT],
                            box.borderColour[BoxStyle.LEFT]);
                    cellStyle.setBorder(Border.RIGHT, box.borderStyle[BoxStyle.RIGHT],
                            box.borderColour[BoxStyle.RIGHT]);
                }
            } catch (Exception e) {
                throw new JRException("Error setting cellFormat-template.", e);
            }

            loadedCellStyles.put(styleKey, cellStyle);
        }

        return cellStyle;
    }

    /**
     *
     */
    protected static BorderLineStyle getBorderLineStyle(JRPen pen) {
        float lineWidth = pen.getLineWidth().floatValue();

        if (lineWidth > 0f) {
            switch (pen.getLineStyleValue()) {
            case DOUBLE: {
                return BorderLineStyle.DOUBLE;
            }
            case DOTTED: {
                return BorderLineStyle.DOTTED;
            }
            case DASHED: {
                if (lineWidth >= 1f) {
                    return BorderLineStyle.MEDIUM_DASHED;
                }

                return BorderLineStyle.DASHED;
            }
            case SOLID:
            default: {
                if (lineWidth >= 2f) {
                    return BorderLineStyle.THICK;
                } else if (lineWidth >= 1f) {
                    return BorderLineStyle.MEDIUM;
                } else if (lineWidth >= 0.5f) {
                    return BorderLineStyle.THIN;
                }

                return BorderLineStyle.HAIR;
            }
            }
        }

        return BorderLineStyle.NONE;
    }

    private final void setSheetSettings(SheetInfo sheetInfo, WritableSheet sheet) {
        PageOrientation po;
        PaperSize ps;

        if (pageFormat.getOrientation() == OrientationEnum.PORTRAIT) {
            po = PageOrientation.PORTRAIT;
        } else {
            po = PageOrientation.LANDSCAPE;
        }
        if ((ps = getSuitablePaperSize()) != null) {
            sheet.setPageSetup(po, ps, 0, 0);
        } else {
            sheet.setPageSetup(po);
        }
        SheetSettings sheets = sheet.getSettings();

        JxlReportConfiguration configuration = getCurrentItemConfiguration();

        boolean isIgnorePageMargins = configuration.isIgnorePageMargins();

        if (pageFormat.getTopMargin() != null) {
            sheets.setTopMargin(LengthUtil.inchNoRound(isIgnorePageMargins ? 0 : pageFormat.getTopMargin()));
        }

        if (pageFormat.getLeftMargin() != null) {
            sheets.setLeftMargin(LengthUtil.inchNoRound(isIgnorePageMargins ? 0 : pageFormat.getLeftMargin()));
        }

        if (pageFormat.getRightMargin() != null) {
            sheets.setRightMargin(LengthUtil.inchNoRound(isIgnorePageMargins ? 0 : pageFormat.getRightMargin()));
        }

        if (pageFormat.getBottomMargin() != null) {
            sheets.setBottomMargin(LengthUtil.inchNoRound(isIgnorePageMargins ? 0 : pageFormat.getBottomMargin()));
        }

        sheets.setHeaderMargin(0.0);
        sheets.setFooterMargin(0.0);

        Integer fitWidth = configuration.getFitWidth();
        Integer fitHeight = configuration.getFitHeight();

        if (fitWidth != null || fitWidth != null) {
            sheets.setFitWidth(fitWidth == null ? 1 : fitWidth);
            sheets.setFitHeight(fitHeight == null ? 1 : fitHeight);
            sheets.setFitToPages(true);
        }

        String password = configuration.getPassword();
        if (password != null) {
            sheets.setPassword(password);
            sheets.setProtected(true);
        }

        String sheetHeaderLeft = configuration.getSheetHeaderLeft();
        if (sheetHeaderLeft != null) {
            sheets.getHeader().getLeft().append(sheetHeaderLeft);
        }

        String sheetHeaderCenter = configuration.getSheetHeaderCenter();
        if (sheetHeaderCenter != null) {
            sheets.getHeader().getCentre().append(sheetHeaderCenter);
        }

        String sheetHeaderRight = configuration.getSheetHeaderRight();
        if (sheetHeaderRight != null) {
            sheets.getHeader().getRight().append(sheetHeaderRight);
        }

        String sheetFooterLeft = configuration.getSheetFooterLeft();
        if (sheetFooterLeft != null) {
            sheets.getFooter().getLeft().append(sheetFooterLeft);
        }

        String sheetFooterCenter = configuration.getSheetFooterCenter();
        if (sheetFooterCenter != null) {
            sheets.getFooter().getCentre().append(sheetFooterCenter);
        }

        String sheetFooterRight = configuration.getSheetFooterRight();
        if (sheetFooterRight != null) {
            sheets.getFooter().getRight().append(sheetFooterRight);
        }

        if (sheetInfo.sheetFirstPageNumber != null && sheetInfo.sheetFirstPageNumber > 0) {
            sheets.setPageStart(sheetInfo.sheetFirstPageNumber);
            firstPageNotSet = false;
        } else {
            Integer documentFirstPageNumber = configuration.getFirstPageNumber();
            if (documentFirstPageNumber != null && documentFirstPageNumber > 0 && firstPageNotSet) {
                sheets.setPageStart(documentFirstPageNumber);
                firstPageNotSet = false;
            }
        }
        if (!firstPageNotSet && sheets.getFooter().getCentre().empty()) {
            sheets.getFooter().getCentre().append("Page ");
            sheets.getFooter().getCentre().appendPageNumber();
        }

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

        maxRowFreezeIndex = 0;
        maxColumnFreezeIndex = 0;

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

    private final PaperSize getSuitablePaperSize() {

        if (pageFormat == null) {
            return null;
        }
        long width = 0;
        long height = 0;
        PaperSize ps = null;

        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 JExcelApi yet.
            for (int i = 3; i < 6; i++) {
                int w = calculateWidthForDinAN(i);
                int h = calculateHeightForDinAN(i);

                if (((w == width) && (h == height)) || ((h == width) && (w == height))) {
                    if (i == 3) {
                        ps = PaperSize.A3;
                    } else if (i == 4) {
                        ps = PaperSize.A4;
                    } else if (i == 5) {
                        ps = PaperSize.A5;
                    }
                    break;
                }
            }

            // Compare to common North American Paper Sizes (ANSI X3.151-1987).
            if (ps == null) {
                // ANSI X3.151-1987 - "Letter" (216  279 mm)
                if (((width == 216) && (height == 279)) || ((width == 279) && (height == 216))) {
                    ps = PaperSize.LETTER;
                }
                // ANSI X3.151-1987 - "Legal" (216  356 mm)
                if (((width == 216) && (height == 356)) || ((width == 356) && (height == 216))) {
                    ps = PaperSize.LEGAL;
                }
                // ANSI X3.151-1987 - "Executive" (190  254 mm)
                // Not supperted by JExcelApi yet.

                // ANSI X3.151-1987 - "Ledger/Tabloid" (279  432 mm)
                // Not supperted by JExcelApi yet.
            }
        }
        return ps;
    }

    public static TextAlignHolder getTextAlignHolder(JRPrintText textElement) {
        HorizontalAlignEnum horizontalAlignment;
        VerticalAlignEnum verticalAlignment;
        RotationEnum rotation = textElement.getRotationValue();

        switch (textElement.getRotationValue()) {
        case LEFT: {
            switch (textElement.getHorizontalAlignmentValue()) {
            case LEFT: {
                verticalAlignment = VerticalAlignEnum.BOTTOM;
                break;
            }
            case CENTER: {
                verticalAlignment = VerticalAlignEnum.MIDDLE;
                break;
            }
            case RIGHT: {
                verticalAlignment = VerticalAlignEnum.TOP;
                break;
            }
            case JUSTIFIED: {
                verticalAlignment = VerticalAlignEnum.JUSTIFIED;
                break;
            }
            default: {
                verticalAlignment = VerticalAlignEnum.BOTTOM;
            }
            }

            switch (textElement.getVerticalAlignmentValue()) {
            case TOP: {
                horizontalAlignment = HorizontalAlignEnum.LEFT;
                break;
            }
            case MIDDLE: {
                horizontalAlignment = HorizontalAlignEnum.CENTER;
                break;
            }
            case BOTTOM: {
                horizontalAlignment = HorizontalAlignEnum.RIGHT;
                break;
            }
            default: {
                horizontalAlignment = HorizontalAlignEnum.LEFT;
            }
            }

            break;
        }
        case RIGHT: {
            switch (textElement.getHorizontalAlignmentValue()) {
            case LEFT: {
                verticalAlignment = VerticalAlignEnum.TOP;
                break;
            }
            case CENTER: {
                verticalAlignment = VerticalAlignEnum.MIDDLE;
                break;
            }
            case RIGHT: {
                verticalAlignment = VerticalAlignEnum.BOTTOM;
                break;
            }
            case JUSTIFIED: {
                verticalAlignment = VerticalAlignEnum.JUSTIFIED;
                break;
            }
            default: {
                verticalAlignment = VerticalAlignEnum.TOP;
            }
            }

            switch (textElement.getVerticalAlignmentValue()) {
            case TOP: {
                horizontalAlignment = HorizontalAlignEnum.RIGHT;
                break;
            }
            case MIDDLE: {
                horizontalAlignment = HorizontalAlignEnum.CENTER;
                break;
            }
            case BOTTOM: {
                horizontalAlignment = HorizontalAlignEnum.LEFT;
                break;
            }
            default: {
                horizontalAlignment = HorizontalAlignEnum.RIGHT;
            }
            }

            break;
        }
        case UPSIDE_DOWN:
        case NONE:
        default: {
            horizontalAlignment = textElement.getHorizontalAlignmentValue();
            verticalAlignment = textElement.getVerticalAlignmentValue();
        }
        }

        return new TextAlignHolder(horizontalAlignment, verticalAlignment, rotation);
    }

    protected void exportFrame(JRPrintFrame frame, JRExporterGridCell gridCell, int col, int row)
            throws JRException {
        addMergeRegion(gridCell, col, row);

        Colour forecolor = getWorkbookColour(frame.getForecolor());
        Colour backcolor = WHITE;
        Pattern mode = backgroundMode;

        if (frame.getModeValue() == ModeEnum.OPAQUE) {
            mode = Pattern.SOLID;
            backcolor = getWorkbookColour(frame.getBackcolor(), true);
        }

        WritableFont cellFont = getLoadedFont(getDefaultFont(), forecolor.getValue(), getLocale());
        WritableCellFormat cellStyle = getLoadedCellStyle(mode, backcolor, cellFont, gridCell, isCellLocked(frame));

        Blank cell = new Blank(col, row, cellStyle);
        try {
            sheet.addCell(cell);
        } catch (JXLException e) {
            throw new JRException("Can't add cell.", e);
        }
    }

    protected void exportGenericElement(JRGenericPrintElement element, JRExporterGridCell gridCell, int colIndex,
            int rowIndex, int emptyCols, int yCutsRow, JRGridLayout layout) throws JRException {
        GenericElementJExcelApiHandler handler = (GenericElementJExcelApiHandler) GenericElementHandlerEnviroment
                .getInstance(getJasperReportsContext())
                .getElementHandler(element.getGenericType(), JXL_EXPORTER_KEY);

        if (handler != null) {
            handler.exportElement(exporterContext, element, gridCell, colIndex, rowIndex, emptyCols, yCutsRow,
                    layout);
        } else {
            if (log.isDebugEnabled()) {
                log.debug("No XLS generic element handler for " + element.getGenericType());
            }
        }
    }

    /**
     * 
     */
    public String getExporterKey() {
        return JXL_EXPORTER_KEY;
    }

    /**
     * 
     */
    public String getExporterPropertiesPrefix() {
        return XLS_EXPORTER_PROPERTIES_PREFIX;
    }

    /**
     * 
     */
    protected boolean isComplexFormat(JRPrintElement element) {
        if (element.hasProperties()
                && element.getPropertiesMap().containsProperty(JxlReportConfiguration.PROPERTY_COMPLEX_FORMAT)) {
            // we make this test to avoid reaching the global default value of the property directly
            // and thus skipping the report level one, if present
            return getPropertiesUtil().getBooleanProperty(element, JxlReportConfiguration.PROPERTY_COMPLEX_FORMAT,
                    getCurrentItemConfiguration().isComplexFormat());
        }
        return getCurrentItemConfiguration().isComplexFormat();
    }

    /**
     * Creates a freeze pane for the current sheet. Freeze pane row and column indexes defined at element level override indexes defined at report level. 
     * If multiple row freeze indexes are found in the same sheet, their maximum value is considered. 
     * 
     * @param rowIndex the freeze 0-based row index
     * @param colIndex the freeze 0-based column index
     * @param isRowEdge specifies if the freeze row index is set at element level
     * @param isColumnEdge specifies if the freeze column index is set at element level
     */
    protected void setFreezePane(int rowIndex, int colIndex, boolean isRowEdge, boolean isColumnEdge) {
        int maxRowIndex = isFreezeRowEdge ? Math.max(rowIndex, maxRowFreezeIndex)
                : (isRowEdge ? rowIndex : Math.max(rowIndex, maxRowFreezeIndex));
        int maxColIndex = isFreezeColumnEdge ? Math.max(colIndex, maxColumnFreezeIndex)
                : (isColumnEdge ? colIndex : Math.max(colIndex, maxColumnFreezeIndex));

        SheetSettings settings = sheet.getSettings();
        settings.setVerticalFreeze(maxRowIndex);
        settings.setHorizontalFreeze(maxColIndex);
        maxRowFreezeIndex = maxRowIndex;
        maxColumnFreezeIndex = maxColIndex;
        isFreezeRowEdge = isRowEdge;
        isFreezeColumnEdge = isColumnEdge;
    }

    protected void setSheetName(String sheetName) {
        sheet.setName(sheetName);
    }

    protected void setAutoFilter(String autoFilterRange) {
        // TODO support auto filter feature

    }

    @Override
    protected void setRowLevels(XlsRowLevelInfo levelInfo, String level) {
        Map<String, Integer> levelMap = levelInfo.getLevelMap();
        try {
            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.setRowGroup(startIndex, levelInfo.getEndIndex(), false);
                        }
                    }
                }
            }
        } catch (RowsExceededException e) {
            throw new JRRuntimeException(e);
        } catch (WriteException e) {
            throw new JRRuntimeException(e);
        }
    }

    protected void setScale(Integer scale) {
        if (scale != null && scale > 9 && scale < 401) {
            SheetSettings sheetSettings = sheet.getSettings();
            sheetSettings.setScaleFactor(scale);

            /* the scale factor takes precedence over fitWidth and fitHeight properties */
            sheetSettings.setFitWidth(0);
            sheetSettings.setFitHeight(0);
            sheetSettings.setFitToPages(false);
        }
    }

    protected void setAnchorType(WritableImage image, ImageAnchorTypeEnum anchorType) {
        switch (anchorType) {
        case MOVE_SIZE:
            image.setImageAnchor(WritableImage.MOVE_AND_SIZE_WITH_CELLS);
            break;
        case NO_MOVE_NO_SIZE:
            image.setImageAnchor(WritableImage.NO_MOVE_OR_SIZE_WITH_CELLS);
            break;
        case MOVE_NO_SIZE:
        default:
            image.setImageAnchor(WritableImage.MOVE_WITH_CELLS);
            break;
        }
    }
}