net.sf.jasperreports.engine.export.ooxml.JRXlsxExporter.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.jasperreports.engine.export.ooxml.JRXlsxExporter.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/>.
 */
package net.sf.jasperreports.engine.export.ooxml;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.geom.Dimension2D;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URLEncoder;
import java.text.AttributedCharacterIterator;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import net.sf.jasperreports.engine.DefaultJasperReportsContext;
import net.sf.jasperreports.engine.JRCommonText;
import net.sf.jasperreports.engine.JRException;
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.JRPrintElementIndex;
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.JRPrintPage;
import net.sf.jasperreports.engine.JRPrintText;
import net.sf.jasperreports.engine.JRPropertiesUtil;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JRStyle;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReportsContext;
import net.sf.jasperreports.engine.PrintPageFormat;
import net.sf.jasperreports.engine.base.JRBaseLineBox;
import net.sf.jasperreports.engine.export.Cut;
import net.sf.jasperreports.engine.export.CutsInfo;
import net.sf.jasperreports.engine.export.GenericElementHandlerEnviroment;
import net.sf.jasperreports.engine.export.HyperlinkUtil;
import net.sf.jasperreports.engine.export.JRExporterGridCell;
import net.sf.jasperreports.engine.export.JRGridLayout;
import net.sf.jasperreports.engine.export.JRHyperlinkProducer;
import net.sf.jasperreports.engine.export.JRXlsAbstractExporter;
import net.sf.jasperreports.engine.export.LengthUtil;
import net.sf.jasperreports.engine.export.OccupiedGridCell;
import net.sf.jasperreports.engine.export.XlsRowLevelInfo;
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.export.zip.ExportZipEntry;
import net.sf.jasperreports.engine.export.zip.FileBufferedZipEntry;
import net.sf.jasperreports.engine.type.HyperlinkTypeEnum;
import net.sf.jasperreports.engine.type.LineDirectionEnum;
import net.sf.jasperreports.engine.type.ModeEnum;
import net.sf.jasperreports.engine.type.ScaleImageEnum;
import net.sf.jasperreports.engine.util.DefaultFormatFactory;
import net.sf.jasperreports.engine.util.FileBufferedOutputStream;
import net.sf.jasperreports.engine.util.JRDataUtils;
import net.sf.jasperreports.engine.util.JRStringUtil;
import net.sf.jasperreports.engine.util.JRStyledText;
import net.sf.jasperreports.engine.util.JRTypeSniffer;
import net.sf.jasperreports.export.ExporterInput;
import net.sf.jasperreports.export.ExporterInputItem;
import net.sf.jasperreports.export.XlsReportConfiguration;
import net.sf.jasperreports.export.XlsxExporterConfiguration;
import net.sf.jasperreports.export.XlsxReportConfiguration;
import net.sf.jasperreports.renderers.DataRenderable;
import net.sf.jasperreports.renderers.DimensionRenderable;
import net.sf.jasperreports.renderers.Renderable;
import net.sf.jasperreports.renderers.RenderersCache;
import net.sf.jasperreports.renderers.ResourceRenderer;

/**
 * Exports a JasperReports document to XLSX format. It has character output type and exports the document to a
 * grid-based layout.
 * 
 * @see net.sf.jasperreports.engine.export.JRXlsAbstractExporter
 * @see net.sf.jasperreports.export.XlsExporterConfiguration
 * @see net.sf.jasperreports.export.XlsReportConfiguration
 * @author Teodor Danciu (teodord@users.sourceforge.net)
 */
public class JRXlsxExporter
        extends JRXlsAbstractExporter<XlsxReportConfiguration, XlsxExporterConfiguration, JRXlsxExporterContext> {
    private static final Log log = LogFactory.getLog(JRXlsxExporter.class);

    /**
     * The exporter key, as used in
     * {@link GenericElementHandlerEnviroment#getElementHandler(JRGenericElementType, String)}.
     */
    public static final String XLSX_EXPORTER_KEY = JRPropertiesUtil.PROPERTY_PREFIX + "xlsx";

    protected static final String XLSX_EXPORTER_PROPERTIES_PREFIX = JRPropertiesUtil.PROPERTY_PREFIX
            + "export.xlsx.";

    protected static final String ONE_CELL = "oneCell";

    protected static final String TWO_CELL = "twoCell";

    protected static final String ABSOLUTE = "absolute";

    /**
     *
     */
    protected static final String JR_PAGE_ANCHOR_PREFIX = "JR_PAGE_ANCHOR_";

    /**
     *
     */
    public static final String IMAGE_NAME_PREFIX = "img_";
    protected static final int IMAGE_NAME_PREFIX_LEGTH = IMAGE_NAME_PREFIX.length();

    /**
     *
     */
    protected XlsxZip xlsxZip;
    protected XlsxWorkbookHelper wbHelper;
    protected XlsxRelsHelper relsHelper;
    protected XlsxContentTypesHelper ctHelper;
    protected PropsAppHelper appHelper;
    protected PropsCoreHelper coreHelper;
    protected XlsxSheetHelper sheetHelper;
    protected XlsxSheetRelsHelper sheetRelsHelper;
    protected XlsxDrawingHelper drawingHelper;
    protected XlsxDrawingRelsHelper drawingRelsHelper;
    protected XlsxStyleHelper styleHelper;
    protected XlsxCellHelper cellHelper;//FIXMEXLSX maybe cell helper should be part of sheet helper, just like in table helper
    protected StringBuilder definedNames;
    protected String firstSheetName;
    protected String currentSheetName;

    protected Map<String, String> rendererToImagePathMap;
    //   protected Map imageMaps;
    //   protected Map hyperlinksMap;

    protected int tableIndex;
    protected boolean startPage;

    protected LinkedList<Color> backcolorStack = new LinkedList<Color>();
    protected Color backcolor;

    private XlsxRunHelper runHelper;

    protected String sheetAutoFilter;

    protected String macroTemplate;

    protected PrintPageFormat oldPageFormat;

    protected Integer currentSheetPageScale;

    protected Integer currentSheetFirstPageNumber;

    protected Map<String, Integer> sheetMapping;

    protected class ExporterContext extends BaseExporterContext implements JRXlsxExporterContext {
    }

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

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

        exporterContext = new ExporterContext();

        maxColumnIndex = 16383;
    }

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

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

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

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

        XlsReportConfiguration configuration = getCurrentItemConfiguration();

        styleHelper.setConfiguration(configuration);

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

    @Override
    protected int exportPage(JRPrintPage page, CutsInfo xCuts, int startRow, String defaultSheetName)
            throws JRException {
        if (oldPageFormat != pageFormat) {
            oldPageFormat = pageFormat;
        }

        return super.exportPage(page, xCuts, startRow, defaultSheetName);
    }

    public JRPrintImage getImage(ExporterInput exporterInput, JRPrintElementIndex imageIndex) throws JRException//FIXMECONTEXT move these to an abstract up?
    {
        List<ExporterInputItem> items = exporterInput.getItems();
        ExporterInputItem item = items.get(imageIndex.getReportIndex());
        JasperPrint report = item.getJasperPrint();
        JRPrintPage page = report.getPages().get(imageIndex.getPageIndex());

        Integer[] elementIndexes = imageIndex.getAddressArray();
        Object element = page.getElements().get(elementIndexes[0]);

        for (int i = 1; i < elementIndexes.length; ++i) {
            JRPrintFrame frame = (JRPrintFrame) element;
            element = frame.getElements().get(elementIndexes[i]);
        }

        if (element instanceof JRGenericPrintElement) {
            JRGenericPrintElement genericPrintElement = (JRGenericPrintElement) element;
            return ((GenericElementXlsxHandler) GenericElementHandlerEnviroment.getInstance(jasperReportsContext)
                    .getElementHandler(genericPrintElement.getGenericType(), XLSX_EXPORTER_KEY))
                            .getImage(exporterContext, genericPrintElement);
        }

        return (JRPrintImage) element;
    }

    /**
     *
     */
    protected void exportStyledText(JRStyle style, JRStyledText styledText, Locale locale, boolean isStyledText) {
        String text = styledText.getText();

        int runLimit = 0;

        AttributedCharacterIterator iterator = styledText.getAttributedString().getIterator();

        while (runLimit < styledText.length() && (runLimit = iterator.getRunLimit()) <= styledText.length()) {
            runHelper.export(style, iterator.getAttributes(), text.substring(iterator.getIndex(), runLimit), locale,
                    invalidCharReplacement, isStyledText);

            iterator.setIndex(runLimit);
        }
    }

    protected JRPrintElementIndex getElementIndex(JRExporterGridCell gridCell) {
        JRPrintElementIndex imageIndex = new JRPrintElementIndex(reportIndex, pageIndex,
                gridCell.getElementAddress());
        return imageIndex;
    }

    /**
     *
     *
    protected void writeImageMap(String imageMapName, JRPrintHyperlink mainHyperlink, List imageMapAreas) throws IOException
    {
       writer.write("<map name=\"" + imageMapName + "\">\n");
        
       for (Iterator it = imageMapAreas.iterator(); it.hasNext();)
       {
     JRPrintImageAreaHyperlink areaHyperlink = (JRPrintImageAreaHyperlink) it.next();
     JRPrintImageArea area = areaHyperlink.getArea();
        
     writer.write("  <area shape=\"" + JRPrintImageArea.getHtmlShape(area.getShape()) + "\"");
     writeImageAreaCoordinates(area);
     writeImageAreaHyperlink(areaHyperlink.getHyperlink());
     writer.write("/>\n");
       }
        
       if (mainHyperlink.getHyperlinkTypeValue() != NONE)
       {
     writer.write("  <area shape=\"default\"");
     writeImageAreaHyperlink(mainHyperlink);
     writer.write("/>\n");
       }
        
       writer.write("</map>\n");
    }
        
        
    protected void writeImageAreaCoordinates(JRPrintImageArea area) throws IOException
    {
       int[] coords = area.getCoordinates();
       if (coords != null && coords.length > 0)
       {
     StringBuilder coordsEnum = new StringBuilder(coords.length * 4);
     coordsEnum.append(coords[0]);
     for (int i = 1; i < coords.length; i++)
     {
        coordsEnum.append(',');
        coordsEnum.append(coords[i]);
     }
        
     writer.write(" coords=\"" + coordsEnum + "\"");
       }
    }
        
        
    protected void writeImageAreaHyperlink(JRPrintHyperlink hyperlink) throws IOException
    {
       String href = getHyperlinkURL(hyperlink);
       if (href == null)
       {
     writer.write(" nohref=\"nohref\"");
       }
       else
       {
     writer.write(" href=\"" + href + "\"");
        
     String target = getHyperlinkTarget(hyperlink);
     if (target != null)
     {
        writer.write(" target=\"");
        writer.write(target);
        writer.write("\"");
     }
       }
        
       if (hyperlink.getHyperlinkTooltip() != null)
       {
     writer.write(" title=\"");
     writer.write(JRStringUtil.xmlEncode(hyperlink.getHyperlinkTooltip()));
     writer.write("\"");
       }
    }
        
        
    /**
     *
     */
    public static JRPrintElementIndex getPrintElementIndex(String imageName) {
        if (!imageName.startsWith(IMAGE_NAME_PREFIX)) {
            throw new JRRuntimeException(EXCEPTION_MESSAGE_KEY_INVALID_IMAGE_NAME, new Object[] { imageName });
        }

        return JRPrintElementIndex.parsePrintElementIndex(imageName.substring(IMAGE_NAME_PREFIX_LEGTH));
    }

    /**
     *
     */
    protected void setBackcolor(Color color) {
        backcolorStack.addLast(backcolor);

        backcolor = color;
    }

    protected void restoreBackcolor() {
        backcolor = backcolorStack.removeLast();
    }

    //   protected boolean startHyperlink(JRPrintHyperlink link, boolean isText)
    //   {
    //      String href = getHyperlinkURL(link);
    //
    //      if (href != null)
    //      {
    //         String id = (String)hyperlinksMap.get(href);
    //         if (id == null)
    //         {
    //            id = "rIdLnk" + hyperlinksMap.size();
    //            hyperlinksMap.put(href, id);
    //         }
    ////         
    ////         wbHelper.write("<w:hyperlink r:id=\"" + id + "\"");
    ////
    ////         String target = getHyperlinkTarget(link);//FIXMETARGET
    ////         if (target != null)
    ////         {
    ////            wbHelper.write(" tgtFrame=\"" + target + "\"");
    ////         }
    ////
    ////         wbHelper.write(">\n");
    //
    //         sheetRelsHelper.exportHyperlink(id, href);
    //
    ////         String tooltip = link.getHyperlinkTooltip(); 
    ////         if (tooltip != null)
    ////         {
    ////            wbHelper.write(" \\o \"" + JRStringUtil.xmlEncode(tooltip) + "\"");
    ////         }
    ////
    ////         wbHelper.write(" </w:instrText></w:r>\n");
    ////         wbHelper.write("<w:r><w:fldChar w:fldCharType=\"separate\"/></w:r>\n");
    //      }
    //
    //      return href != null;
    //   }

    protected String getHyperlinkTarget(JRPrintHyperlink link) {
        String target = null;
        switch (link.getHyperlinkTargetValue()) {
        case SELF: {
            target = "_self";
            break;
        }
        case BLANK:
        default: {
            target = "_blank";
            break;
        }
        }
        return target;
    }

    protected String getHyperlinkURL(JRPrintHyperlink link) {
        String href = null;

        XlsReportConfiguration configuration = getCurrentItemConfiguration();

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

        //test for ignore hyperlinks done here
        if (!ignoreHyperlink) {
            JRHyperlinkProducer customHandler = getHyperlinkProducer(link);
            if (customHandler == null) {
                switch (link.getHyperlinkTypeValue()) {
                case REFERENCE: {
                    if (link.getHyperlinkReference() != null) {
                        try {
                            href = link.getHyperlinkReference().replaceAll("\\s", URLEncoder.encode(" ", "UTF-8"));
                        } catch (UnsupportedEncodingException e) {
                            href = link.getHyperlinkReference();
                        }
                    }

                    break;
                }
                case LOCAL_ANCHOR: {
                    if (!configuration.isIgnoreAnchors() && link.getHyperlinkAnchor() != null) //test for ignore anchors done here
                    {
                        href = link.getHyperlinkAnchor();
                    }
                    break;
                }
                case LOCAL_PAGE: {
                    if (!configuration.isIgnoreAnchors() && link.getHyperlinkPage() != null) //test for ignore anchors done here
                    {
                        href = JR_PAGE_ANCHOR_PREFIX + reportIndex + "_"
                                + (configuration.isOnePagePerSheet() ? link.getHyperlinkPage().toString() : "1");
                    }
                    break;
                }
                case REMOTE_ANCHOR: {
                    if (link.getHyperlinkReference() != null && link.getHyperlinkAnchor() != null) {
                        try {
                            href = link.getHyperlinkReference().replaceAll("\\s", URLEncoder.encode(" ", "UTF-8"));
                        } catch (UnsupportedEncodingException e) {
                            href = link.getHyperlinkReference();
                        }
                        href = href + "#" + link.getHyperlinkAnchor();
                    }
                    break;
                }
                case REMOTE_PAGE: {
                    //                  if (
                    //                     link.getHyperlinkReference() != null &&
                    //                     link.getHyperlinkPage() != null
                    //                     )
                    //                  {
                    //                     href = link.getHyperlinkReference() + "#" + JR_PAGE_ANCHOR_PREFIX + "0_" + link.getHyperlinkPage().toString();
                    //                  }
                    break;
                }
                case NONE:
                default: {
                    break;
                }
                }
            } else {
                href = customHandler.getHyperlink(link);
            }
        }

        return href;
    }

    //   protected void endHyperlink(boolean isText)
    //   {
    ////      wbHelper.write("</w:hyperlink>\n");
    //      wbHelper.write("<w:r><w:fldChar w:fldCharType=\"end\"/></w:r>\n");
    //   }

    protected void insertPageAnchor(int colIndex, int rowIndex) {
        if (!getCurrentItemConfiguration().isIgnoreAnchors() && startPage) {
            String anchorPage = JR_PAGE_ANCHOR_PREFIX + reportIndex + "_"
                    + (sheetIndex - sheetsBeforeCurrentReport);
            String ref = "'" + JRStringUtil.xmlEncode(currentSheetName) + "'!$A$1"; // + XlsxCellHelper.getColumIndexLetter(colIndex) + "$" + (rowIndex + 1);
            definedNames
                    .append("<definedName name=\"" + getDefinedName(anchorPage) + "\">" + ref + "</definedName>\n");
            startPage = false;
        }
    }

    @Override
    protected void addBlankCell(JRExporterGridCell gridCell, int colIndex, int rowIndex) throws JRException {
        cellHelper.exportHeader(gridCell, rowIndex, colIndex, maxColumnIndex, sheetInfo);
        cellHelper.exportFooter();
    }

    @Override
    protected void closeWorkbook(OutputStream os) throws JRException //FIXMEXLSX could throw IOException here, as other implementations do
    {
        if (sheetMapping != null && definedNamesMap != null && !definedNamesMap.isEmpty()) {
            for (Map.Entry<NameScope, String> entry : definedNamesMap.entrySet()) {
                String name = entry.getKey().getName();
                String localSheetId = "";
                if (name != null && entry.getValue() != null) {
                    String scope = entry.getKey().getScope();
                    // name and name scope are ignoring case in Excel
                    if (scope != null && !scope.equalsIgnoreCase(DEFAULT_DEFINED_NAME_SCOPE)
                            && sheetMapping.containsKey(scope)) {
                        localSheetId = " localSheetId=\"" + sheetMapping.get(scope) + "\"";
                    }
                    definedNames.append("<definedName name=\"" + name + "\"" + localSheetId + ">" + entry.getValue()
                            + "</definedName>\n");
                }
            }
        }

        styleHelper.export();

        styleHelper.close();

        try {
            wbHelper.exportFooter();

            wbHelper.close();

            //         if ((hyperlinksMap != null && hyperlinksMap.size() > 0))
            //         {
            //            for(Iterator it = hyperlinksMap.keySet().iterator(); it.hasNext();)
            //            {
            //               String href = (String)it.next();
            //               String id = (String)hyperlinksMap.get(href);
            //
            //               relsHelper.exportHyperlink(id, href);
            //            }
            //         }

            relsHelper.exportFooter();
            relsHelper.close();

            ctHelper.exportFooter();
            ctHelper.close();

            appHelper.exportFooter();
            appHelper.close();

            coreHelper.exportFooter();
            coreHelper.close();

            xlsxZip.zipEntries(os);

            xlsxZip.dispose();

        } catch (IOException e) {
            throw new JRException(e);
        }
    }

    @Override
    protected void createSheet(CutsInfo xCuts, SheetInfo sheetInfo) {
        startPage = true;
        currentSheetPageScale = sheetInfo.sheetPageScale;
        currentSheetFirstPageNumber = sheetInfo.sheetFirstPageNumber;
        currentSheetName = sheetInfo.sheetName;
        firstSheetName = firstSheetName == null ? currentSheetName : firstSheetName;
        wbHelper.exportSheet(sheetIndex + 1, currentSheetName, sheetMapping);
        ctHelper.exportSheet(sheetIndex + 1);
        relsHelper.exportSheet(sheetIndex + 1);
        XlsxReportConfiguration configuration = getCurrentItemConfiguration();
        ExportZipEntry sheetRelsEntry = xlsxZip.addSheetRels(sheetIndex + 1);
        Writer sheetRelsWriter = sheetRelsEntry.getWriter();
        sheetRelsHelper = new XlsxSheetRelsHelper(jasperReportsContext, sheetRelsWriter);

        ExportZipEntry sheetEntry = xlsxZip.addSheet(sheetIndex + 1);
        Writer sheetWriter = sheetEntry.getWriter();
        sheetHelper = new XlsxSheetHelper(jasperReportsContext, sheetWriter, sheetRelsHelper, configuration);

        ExportZipEntry drawingRelsEntry = xlsxZip.addDrawingRels(sheetIndex + 1);
        Writer drawingRelsWriter = drawingRelsEntry.getWriter();
        drawingRelsHelper = new XlsxDrawingRelsHelper(jasperReportsContext, drawingRelsWriter);

        ExportZipEntry drawingEntry = xlsxZip.addDrawing(sheetIndex + 1);
        Writer drawingWriter = drawingEntry.getWriter();
        drawingHelper = new XlsxDrawingHelper(jasperReportsContext, drawingWriter, drawingRelsHelper);

        cellHelper = new XlsxCellHelper(jasperReportsContext, sheetWriter, styleHelper);

        runHelper = new XlsxRunHelper(jasperReportsContext, sheetWriter, getExporterKey());

        boolean showGridlines = true;
        if (sheetInfo.sheetShowGridlines == null) {
            Boolean documentShowGridlines = configuration.isShowGridLines();
            if (documentShowGridlines != null) {
                showGridlines = documentShowGridlines;
            }
        } else {
            showGridlines = sheetInfo.sheetShowGridlines;
        }
        sheetHelper.exportHeader(showGridlines, (sheetInfo.sheetPageScale == null ? 0 : sheetInfo.sheetPageScale),
                sheetInfo.rowFreezeIndex, sheetInfo.columnFreezeIndex, maxColumnIndex, jasperPrint,
                sheetInfo.tabColor);
        sheetRelsHelper.exportHeader(sheetIndex + 1);
        drawingHelper.exportHeader();
        drawingRelsHelper.exportHeader();
    }

    @Override
    protected void closeSheet() {
        if (sheetHelper != null) {
            XlsReportConfiguration configuration = getCurrentItemConfiguration();

            boolean isIgnorePageMargins = configuration.isIgnorePageMargins();

            if (currentSheetFirstPageNumber != null && currentSheetFirstPageNumber > 0) {
                sheetHelper.exportFooter(sheetIndex, oldPageFormat == null ? pageFormat : oldPageFormat,
                        isIgnorePageMargins, sheetAutoFilter, currentSheetPageScale, currentSheetFirstPageNumber,
                        false, pageIndex - sheetInfo.sheetFirstPageIndex, sheetInfo.printSettings);
                firstPageNotSet = false;
            } else {
                Integer documentFirstPageNumber = configuration.getFirstPageNumber();
                if (documentFirstPageNumber != null && documentFirstPageNumber > 0 && firstPageNotSet) {
                    sheetHelper.exportFooter(sheetIndex, oldPageFormat == null ? pageFormat : oldPageFormat,
                            isIgnorePageMargins, sheetAutoFilter, currentSheetPageScale, documentFirstPageNumber,
                            false, pageIndex - sheetInfo.sheetFirstPageIndex, sheetInfo.printSettings);
                    firstPageNotSet = false;
                } else {
                    sheetHelper.exportFooter(sheetIndex, oldPageFormat == null ? pageFormat : oldPageFormat,
                            isIgnorePageMargins, sheetAutoFilter, currentSheetPageScale, null, firstPageNotSet,
                            pageIndex - sheetInfo.sheetFirstPageIndex, sheetInfo.printSettings);
                }
            }
            if (sheetAutoFilter != null) {
                int index = Math.max(0, sheetIndex - 1);
                definedNames.append("<definedName name=\"_xlnm._FilterDatabase\" localSheetId=\"" + index + "\">'"
                        + JRStringUtil.xmlEncode(currentSheetName) + "'!" + sheetAutoFilter + "</definedName>\n");
            }
            sheetHelper.close();

            sheetRelsHelper.exportFooter();
            sheetRelsHelper.close();

            drawingHelper.exportFooter();
            drawingHelper.close();

            drawingRelsHelper.exportFooter();
            drawingRelsHelper.close();
        }
    }

    @Override
    protected void exportFrame(JRPrintFrame frame, JRExporterGridCell gridCell, int colIndex, int rowIndex)
            throws JRException {
        cellHelper.exportHeader(gridCell, rowIndex, colIndex, maxColumnIndex, sheetInfo);
        sheetHelper.exportMergedCells(rowIndex, colIndex, maxColumnIndex, gridCell.getRowSpan(),
                gridCell.getColSpan());

        //      boolean appendBackcolor =
        //         frame.getModeValue() == ModeEnum.OPAQUE
        //         && (backcolor == null || frame.getBackcolor().getRGB() != backcolor.getRGB());
        //
        //      if (appendBackcolor)
        //      {
        //         setBackcolor(frame.getBackcolor());
        //      }
        //
        //      try
        //      {
        //         JRGridLayout layout = gridCell.getLayout();
        //         JRPrintElementIndex frameIndex =
        //            new JRPrintElementIndex(
        //                  reportIndex,
        //                  pageIndex,
        //                  gridCell.getWrapper().getAddress()
        //                  );
        //         exportGrid(layout, frameIndex);
        //      }
        //      finally
        //      {
        //         if (appendBackcolor)
        //         {
        //            restoreBackcolor();
        //         }
        //      }

        cellHelper.exportFooter();
    }

    @Override
    public void exportImage(JRPrintImage image, JRExporterGridCell gridCell, int colIndex, int rowIndex,
            int emptyCols, int yCutsRow, JRGridLayout layout) throws JRException {
        int topPadding = Math.max(image.getLineBox().getTopPadding(),
                getImageBorderCorrection(image.getLineBox().getTopPen()));
        int leftPadding = Math.max(image.getLineBox().getLeftPadding(),
                getImageBorderCorrection(image.getLineBox().getLeftPen()));
        int bottomPadding = Math.max(image.getLineBox().getBottomPadding(),
                getImageBorderCorrection(image.getLineBox().getBottomPen()));
        int rightPadding = Math.max(image.getLineBox().getRightPadding(),
                getImageBorderCorrection(image.getLineBox().getRightPen()));

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

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

        cellHelper.exportHeader(gridCell, rowIndex, colIndex, maxColumnIndex, sheetInfo);

        Renderable renderer = image.getRenderer();

        if (renderer != null && availableImageWidth > 0 && availableImageHeight > 0) {
            InternalImageProcessor imageProcessor = new InternalImageProcessor(image,
                    image.getScaleImageValue() != ScaleImageEnum.FILL_FRAME, gridCell, availableImageWidth,
                    availableImageHeight);

            InternalImageProcessorResult imageProcessorResult = null;

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

            if (imageProcessorResult != null) {
                int width = availableImageWidth;
                int height = availableImageHeight;

                double cropTop = 0;
                double cropLeft = 0;
                double cropBottom = 0;
                double cropRight = 0;

                switch (image.getScaleImageValue()) {
                case FILL_FRAME: {
                    width = availableImageWidth;
                    height = availableImageHeight;
                    break;
                }
                case CLIP: {
                    double normalWidth = availableImageWidth;
                    double normalHeight = availableImageHeight;

                    Dimension2D dimension = imageProcessorResult.dimension;
                    if (dimension != null) {
                        normalWidth = dimension.getWidth();
                        normalHeight = dimension.getHeight();
                    }

                    //                  if (normalWidth > availableImageWidth)
                    //                  {
                    switch (image.getHorizontalImageAlign()) {
                    case RIGHT: {
                        cropLeft = 100000 * (availableImageWidth - normalWidth) / availableImageWidth;
                        cropRight = 0;
                        break;
                    }
                    case CENTER: {
                        cropLeft = 100000 * (availableImageWidth - normalWidth) / availableImageWidth / 2;
                        cropRight = cropLeft;
                        break;
                    }
                    case LEFT:
                    default: {
                        cropLeft = 0;
                        cropRight = 100000 * (availableImageWidth - normalWidth) / availableImageWidth;
                        break;
                    }
                    }
                    //                  }
                    //                  else
                    //                  {
                    //                     width = (int)normalWidth;
                    //                  }

                    //                  if (normalHeight > availableImageHeight)
                    //                  {
                    switch (image.getVerticalImageAlign()) {
                    case TOP: {
                        cropTop = 0;
                        cropBottom = 100000 * (availableImageHeight - normalHeight) / availableImageHeight;
                        break;
                    }
                    case MIDDLE: {
                        cropTop = 100000 * (availableImageHeight - normalHeight) / availableImageHeight / 2;
                        cropBottom = cropTop;
                        break;
                    }
                    case BOTTOM:
                    default: {
                        cropTop = 100000 * (availableImageHeight - normalHeight) / availableImageHeight;
                        cropBottom = 0;
                        break;
                    }
                    }
                    //                  }
                    //                  else
                    //                  {
                    //                     height = (int)normalHeight;
                    //                  }

                    break;
                }
                case RETAIN_SHAPE:
                default: {
                    double normalWidth = availableImageWidth;
                    double normalHeight = availableImageHeight;

                    Dimension2D dimension = imageProcessorResult.dimension;
                    if (dimension != null) {
                        normalWidth = dimension.getWidth();
                        normalHeight = dimension.getHeight();
                    }

                    double ratio = normalWidth / normalHeight;

                    if (ratio > availableImageWidth / (double) availableImageHeight) {
                        width = availableImageWidth;
                        height = (int) (width / ratio);

                        switch (image.getVerticalImageAlign()) {
                        case TOP: {
                            cropTop = 0;
                            cropBottom = 100000 * (availableImageHeight - height) / availableImageHeight;
                            break;
                        }
                        case MIDDLE: {
                            cropTop = 100000 * (availableImageHeight - height) / availableImageHeight / 2;
                            cropBottom = cropTop;
                            break;
                        }
                        case BOTTOM:
                        default: {
                            cropTop = 100000 * (availableImageHeight - height) / availableImageHeight;
                            cropBottom = 0;
                            break;
                        }
                        }
                    } else {
                        height = availableImageHeight;
                        width = (int) (ratio * height);

                        switch (image.getHorizontalImageAlign()) {
                        case RIGHT: {
                            cropLeft = 100000 * (availableImageWidth - width) / availableImageWidth;
                            cropRight = 0;
                            break;
                        }
                        case CENTER: {
                            cropLeft = 100000 * (availableImageWidth - width) / availableImageWidth / 2;
                            cropRight = cropLeft;
                            break;
                        }
                        case LEFT:
                        default: {
                            cropLeft = 0;
                            cropRight = 100000 * (availableImageWidth - width) / availableImageWidth;
                            break;
                        }
                        }
                    }
                }
                }

                XlsReportConfiguration configuration = getCurrentItemConfiguration();

                if (!configuration.isIgnoreAnchors()) {
                    insertPageAnchor(colIndex, rowIndex);
                    if (image.getAnchorName() != null) {
                        String ref = "'" + JRStringUtil.xmlEncode(currentSheetName) + "'!$"
                                + JRXlsAbstractExporter.getColumIndexName(colIndex, maxColumnIndex) + "$"
                                + (rowIndex + 1);
                        definedNames.append("<definedName name=\"" + getDefinedName(image.getAnchorName()) + "\">"
                                + ref + "</definedName>\n");
                    }
                }

                //            boolean startedHyperlink = startHyperlink(image,false);

                drawingRelsHelper.exportImage(imageProcessorResult.imagePath);

                sheetHelper.exportMergedCells(rowIndex, colIndex, maxColumnIndex, gridCell.getRowSpan(),
                        gridCell.getColSpan());

                ImageAnchorTypeEnum imageAnchorType = ImageAnchorTypeEnum.getByName(
                        JRPropertiesUtil.getOwnProperty(image, XlsReportConfiguration.PROPERTY_IMAGE_ANCHOR_TYPE));
                if (imageAnchorType == null) {
                    imageAnchorType = configuration.getImageAnchorType();
                    if (imageAnchorType == null) {
                        imageAnchorType = ImageAnchorTypeEnum.MOVE_NO_SIZE;
                    }
                }
                drawingHelper.write("<xdr:twoCellAnchor editAs=\"" + getAnchorType(imageAnchorType) + "\">\n");
                drawingHelper.write("<xdr:from><xdr:col>" + colIndex + "</xdr:col><xdr:colOff>"
                        + LengthUtil.emu(leftPadding) + "</xdr:colOff><xdr:row>" + rowIndex
                        + "</xdr:row><xdr:rowOff>" + LengthUtil.emu(topPadding) + "</xdr:rowOff></xdr:from>\n");
                drawingHelper.write("<xdr:to><xdr:col>" + (colIndex + gridCell.getColSpan())
                        + "</xdr:col><xdr:colOff>" + LengthUtil.emu(-rightPadding) + "</xdr:colOff><xdr:row>"
                        + (rowIndex + (configuration.isCollapseRowSpan() ? 1 : gridCell.getRowSpan()))
                        + "</xdr:row><xdr:rowOff>" + LengthUtil.emu(-bottomPadding) + "</xdr:rowOff></xdr:to>\n");

                drawingHelper.write("<xdr:pic>\n");
                drawingHelper.write("<xdr:nvPicPr><xdr:cNvPr id=\""
                        + (image.hashCode() > 0 ? image.hashCode() : -image.hashCode()) + "\" name=\"Picture\">\n");

                String href = HyperlinkTypeEnum.LOCAL_ANCHOR.equals(image.getHyperlinkTypeValue())
                        || HyperlinkTypeEnum.LOCAL_PAGE.equals(image.getHyperlinkTypeValue())
                                ? "#" + getHyperlinkURL(image)
                                : getHyperlinkURL(image);
                if (href != null) {
                    drawingHelper.exportHyperlink(href);
                }

                drawingHelper.write("</xdr:cNvPr><xdr:cNvPicPr/></xdr:nvPicPr>\n");
                drawingHelper.write("<xdr:blipFill>\n");
                drawingHelper.write("<a:blip r:embed=\"" + imageProcessorResult.imagePath + "\"/>");
                drawingHelper.write("<a:srcRect");
                ////            if (cropLeft > 0)
                //               drawingHelper.write(" l=\"" + (int)cropLeft + "\"");
                ////            if (cropTop > 0)
                //               drawingHelper.write(" t=\"" + (int)cropTop + "\"");
                ////            if (cropRight > 0)
                //               drawingHelper.write(" r=\"" + (int)cropRight + "\"");
                ////            if (cropBottom > 0)
                //               drawingHelper.write(" b=\"" + (int)cropBottom + "\"");
                drawingHelper.write("/>");
                drawingHelper.write("<a:stretch><a:fillRect");
                //            if (cropLeft > 0)
                drawingHelper.write(" l=\"" + (int) cropLeft + "\"");
                //            if (cropTop > 0)
                drawingHelper.write(" t=\"" + (int) cropTop + "\"");
                //            if (cropRight > 0)
                drawingHelper.write(" r=\"" + (int) cropRight + "\"");
                //            if (cropBottom > 0)
                drawingHelper.write(" b=\"" + (int) cropBottom + "\"");
                drawingHelper.write("/></a:stretch>\n");
                drawingHelper.write("</xdr:blipFill>\n");
                drawingHelper.write("<xdr:spPr><a:xfrm><a:off x=\"0\" y=\"0\"/><a:ext cx=\"" + LengthUtil.emu(0)
                        + "\" cy=\"" + LengthUtil.emu(0) + "\"/>");
                drawingHelper.write("</a:xfrm><a:prstGeom prst=\"rect\"></a:prstGeom>\n");
                //            if (image.getModeValue() == ModeEnum.OPAQUE && image.getBackcolor() != null)
                //            {
                //               drawingHelper.write("<a:solidFill><a:srgbClr val=\"" + JRColorUtil.getColorHexa(image.getBackcolor()) + "\"/></a:solidFill>\n");
                //            }
                drawingHelper.write("</xdr:spPr>\n");
                drawingHelper.write("</xdr:pic>\n");
                drawingHelper.write("<xdr:clientData/>\n");
                drawingHelper.write("</xdr:twoCellAnchor>\n");

                //            if(startedHyperlink)
                //            {
                //               endHyperlink(false);
                //            }
            }
        }

        //      drawingHelper.write("</w:p>");

        cellHelper.exportFooter();
    }

    private class InternalImageProcessor {
        private final JRPrintElement imageElement;
        private final RenderersCache imageRenderersCache;
        private final boolean needDimension;
        private final JRExporterGridCell cell;
        private final int availableImageWidth;
        private final int availableImageHeight;

        protected InternalImageProcessor(JRPrintImage imageElement, boolean needDimension, JRExporterGridCell cell,
                int availableImageWidth, int availableImageHeight) {
            this.imageElement = imageElement;
            this.imageRenderersCache = imageElement.isUsingCache() ? renderersCache
                    : new RenderersCache(getJasperReportsContext());
            this.needDimension = needDimension;
            this.cell = cell;
            this.availableImageWidth = availableImageWidth;
            this.availableImageHeight = availableImageHeight;
        }

        private InternalImageProcessorResult process(Renderable renderer) throws JRException {
            if (renderer instanceof ResourceRenderer) {
                renderer = imageRenderersCache.getLoadedRenderer((ResourceRenderer) renderer);
            }

            // check dimension first, to avoid caching renderers that might not be used eventually, due to their dimension errors 
            Dimension2D dimension = null;
            if (needDimension) {
                DimensionRenderable dimensionRenderer = imageRenderersCache.getDimensionRenderable(renderer);
                dimension = dimensionRenderer == null ? null : dimensionRenderer.getDimension(jasperReportsContext);
            }

            String imagePath = null;

            //         if (image.isLazy()) //FIXMEXLSX learn how to link images
            //         {
            //            
            //         }
            //         else
            //         {
            if (renderer instanceof DataRenderable //we do not cache imagePath for non-data renderers because they render width different width/height each time
                    && rendererToImagePathMap.containsKey(renderer.getId())) {
                imagePath = rendererToImagePathMap.get(renderer.getId());
            } else {
                JRPrintElementIndex imageIndex = getElementIndex(cell);

                DataRenderable imageRenderer = getRendererUtil().getImageDataRenderable(imageRenderersCache,
                        renderer, new Dimension(availableImageWidth, availableImageHeight),
                        ModeEnum.OPAQUE == imageElement.getModeValue() ? imageElement.getBackcolor() : null);

                byte[] imageData = imageRenderer.getData(jasperReportsContext);
                String fileExtension = JRTypeSniffer.getImageTypeValue(imageData).getFileExtension();
                String imageName = IMAGE_NAME_PREFIX + imageIndex.toString()
                        + (fileExtension == null ? "" : ("." + fileExtension));

                xlsxZip.addEntry(//FIXMEDOCX optimize with a different implementation of entry
                        new FileBufferedZipEntry("xl/media/" + imageName, imageData));

                //               drawingRelsHelper.exportImage(imageName);

                imagePath = imageName;
                //imagePath = "Pictures/" + imageName;

                if (imageRenderer == renderer) {
                    //cache imagePath only for true ImageRenderable instances because the wrapping ones render with different width/height each time
                    rendererToImagePathMap.put(renderer.getId(), imagePath);
                }
            }
            //         }

            return new InternalImageProcessorResult(imagePath, dimension);
        }
    }

    private class InternalImageProcessorResult {
        protected final String imagePath;
        protected final Dimension2D dimension;

        protected InternalImageProcessorResult(String imagePath, Dimension2D dimension) {
            this.imagePath = imagePath;
            this.dimension = dimension;
        }
    }

    @Override
    protected void exportLine(JRPrintLine line, JRExporterGridCell gridCell, int colIndex, int rowIndex)
            throws JRException {
        JRLineBox box = new JRBaseLineBox(null);
        JRPen pen = null;
        LineDirectionEnum direction = null;
        float ratio = line.getWidth() / line.getHeight();
        if (ratio > 1) {
            if (line.getHeight() > 1) {
                direction = line.getDirectionValue();
                pen = box.getPen();
            } else if (line.getDirectionValue() == LineDirectionEnum.TOP_DOWN) {
                pen = box.getTopPen();
            } else {
                pen = box.getBottomPen();
            }
        } else {
            if (line.getWidth() > 1) {
                direction = line.getDirectionValue();
                pen = box.getPen();
            } else if (line.getDirectionValue() == LineDirectionEnum.TOP_DOWN) {
                pen = box.getLeftPen();
            } else {
                pen = box.getRightPen();
            }
        }
        pen.setLineColor(line.getLinePen().getLineColor());
        pen.setLineStyle(line.getLinePen().getLineStyleValue());
        pen.setLineWidth(line.getLinePen().getLineWidth());

        gridCell.setBox(box);//CAUTION: only some exporters set the cell box

        cellHelper.exportHeader(gridCell, rowIndex, colIndex, maxColumnIndex, sheetInfo, direction);
        sheetHelper.exportMergedCells(rowIndex, colIndex, maxColumnIndex, gridCell.getRowSpan(),
                gridCell.getColSpan());
        cellHelper.exportFooter();
    }

    @Override
    protected void exportRectangle(JRPrintGraphicElement rectangle, JRExporterGridCell gridCell, int colIndex,
            int rowIndex) throws JRException {
        JRLineBox box = new JRBaseLineBox(null);
        JRPen pen = box.getPen();
        pen.setLineColor(rectangle.getLinePen().getLineColor());
        pen.setLineStyle(rectangle.getLinePen().getLineStyleValue());
        pen.setLineWidth(rectangle.getLinePen().getLineWidth());

        gridCell.setBox(box);//CAUTION: only some exporters set the cell box

        cellHelper.exportHeader(gridCell, rowIndex, colIndex, maxColumnIndex, sheetInfo);
        sheetHelper.exportMergedCells(rowIndex, colIndex, maxColumnIndex, gridCell.getRowSpan(),
                gridCell.getColSpan());
        cellHelper.exportFooter();
    }

    @Override
    public void exportText(final JRPrintText text, JRExporterGridCell gridCell, int colIndex, int rowIndex)
            throws JRException {
        final JRStyledText styledText = getStyledText(text);

        //      final int textLength = styledText == null ? 0 : styledText.length();

        final String textStr = styledText.getText();

        TextValue textValue = null;
        String pattern = null;

        XlsReportConfiguration configuration = getCurrentItemConfiguration();

        if (configuration.isDetectCellType()) {
            textValue = getTextValue(text, textStr);
            if (textValue instanceof NumberTextValue) {
                pattern = ((NumberTextValue) textValue).getPattern();
            } else if (textValue instanceof DateTextValue) {
                pattern = ((DateTextValue) textValue).getPattern();
            }
        }

        //FIXME: use localized pattern symbols similar to XLS export (via DateFormatConverter class)
        final String convertedPattern = getConvertedPattern(text, pattern);

        cellHelper.exportHeader(gridCell, rowIndex, colIndex, maxColumnIndex, textValue, convertedPattern,
                getTextLocale(text),
                isWrapText(gridCell.getElement()) || Boolean.TRUE
                        .equals(((JRXlsxExporterNature) nature).getColumnAutoFit(gridCell.getElement())),
                isCellHidden(gridCell.getElement()), isCellLocked(gridCell.getElement()),
                isShrinkToFit(gridCell.getElement()), isIgnoreTextFormatting(text), text.getRotationValue(),
                sheetInfo);
        sheetHelper.exportMergedCells(rowIndex, colIndex, maxColumnIndex, gridCell.getRowSpan(),
                gridCell.getColSpan());

        String textFormula = getFormula(text);
        if (textFormula != null) {
            sheetHelper.write("<f>" + textFormula + "</f>\n");
        }

        //      if (text.getLineSpacing() != JRTextElement.LINE_SPACING_SINGLE)
        //      {
        //         styleBuffer.append("line-height: " + text.getLineSpacingFactor() + "; ");
        //      }

        //      if (styleBuffer.length() > 0)
        //      {
        //         writer.write(" style=\"");
        //         writer.write(styleBuffer.toString());
        //         writer.write("\"");
        //      }
        //
        //      writer.write(">");

        //      tableHelper.getParagraphHelper().exportProps(text);
        if (!configuration.isIgnoreAnchors()) {
            insertPageAnchor(colIndex, rowIndex);
            if (text.getAnchorName() != null) {
                String ref = "'" + JRStringUtil.xmlEncode(currentSheetName) + "'!$"
                        + JRXlsAbstractExporter.getColumIndexName(colIndex, maxColumnIndex) + "$" + (rowIndex + 1);
                definedNames.append("<definedName name=\"" + getDefinedName(text.getAnchorName()) + "\">" + ref
                        + "</definedName>\n");
            }
        }

        String href = getHyperlinkURL(text);
        if (href != null) {
            sheetHelper.exportHyperlink(rowIndex, colIndex, maxColumnIndex, href,
                    HyperlinkTypeEnum.LOCAL_ANCHOR.equals(text.getHyperlinkTypeValue())
                            || HyperlinkTypeEnum.LOCAL_PAGE.equals(text.getHyperlinkTypeValue()));
        }

        TextValueHandler handler = new TextValueHandler() {
            @Override
            public void handle(BooleanTextValue textValue) throws JRException {
                if (textValue.getValue() != null) {
                    sheetHelper.write("<v>" + textValue.getValue() + "</v>");
                }
            }

            @Override
            public void handle(DateTextValue textValue) throws JRException {
                Date date = textValue.getValue();
                if (date != null) {
                    sheetHelper.write("<v>" + (date == null ? ""
                            : JRDataUtils.getExcelSerialDayNumber(date, getTextLocale(text), getTextTimeZone(text)))
                            + "</v>");
                }
            }

            @Override
            public void handle(NumberTextValue textValue) throws JRException {

                if (textValue.getValue() != null) {
                    sheetHelper.write("<v>");
                    double doubleValue = textValue.getValue().doubleValue();
                    if (DefaultFormatFactory.STANDARD_NUMBER_FORMAT_DURATION.equals(convertedPattern)) {
                        doubleValue = doubleValue / 86400;
                    }
                    sheetHelper.write(String.valueOf(doubleValue));
                    sheetHelper.write("</v>");
                }
            }

            @Override
            public void handle(StringTextValue textValue) throws JRException {
                writeText();
            }

            private void writeText() throws JRException {
                if (textStr != null && textStr.length() > 0) {
                    sheetHelper.write("<is>"); //FIXMENOW make writer util; check everywhere
                    String markup = text.getMarkup();
                    boolean isStyledText = markup != null && !JRCommonText.MARKUP_NONE.equals(markup)
                            && !isIgnoreTextFormatting(text);
                    exportStyledText(text.getStyle(), styledText, getTextLocale(text), isStyledText);
                    sheetHelper.write("</is>");
                }
            }
        };

        if (textValue != null) {
            //detect cell type
            textValue.handle(handler);
        } else {
            handler.handle((StringTextValue) null);
        }

        cellHelper.exportFooter();
    }

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

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

    @Override
    protected void openWorkbook(OutputStream os) throws JRException {
        rendererToImagePathMap = new HashMap<String, String>();
        //      imageMaps = new HashMap();
        //      hyperlinksMap = new HashMap();
        definedNames = new StringBuilder();
        sheetMapping = new HashMap<String, Integer>();
        try {
            String memoryThreshold = jasperPrint.getPropertiesMap()
                    .getProperty(FileBufferedOutputStream.PROPERTY_MEMORY_THRESHOLD);
            xlsxZip = new XlsxZip(jasperReportsContext, getRepository(),
                    memoryThreshold == null ? null : JRPropertiesUtil.asInteger(memoryThreshold));

            wbHelper = new XlsxWorkbookHelper(jasperReportsContext, xlsxZip.getWorkbookEntry().getWriter(),
                    definedNames);
            wbHelper.exportHeader();

            relsHelper = new XlsxRelsHelper(jasperReportsContext, xlsxZip.getRelsEntry().getWriter());
            ctHelper = new XlsxContentTypesHelper(jasperReportsContext, xlsxZip.getContentTypesEntry().getWriter());

            appHelper = new PropsAppHelper(jasperReportsContext, xlsxZip.getAppEntry().getWriter());
            coreHelper = new PropsCoreHelper(jasperReportsContext, xlsxZip.getCoreEntry().getWriter());

            XlsxExporterConfiguration configuration = getCurrentConfiguration();

            String macro = macroTemplate == null ? configuration.getMacroTemplate() : macroTemplate;
            if (macro != null) {
                xlsxZip.addMacro(macro);
                relsHelper.setContainsMacro(true);
                ctHelper.setContainsMacro(true);
            }
            relsHelper.exportHeader();
            ctHelper.exportHeader();

            appHelper.exportHeader();

            String application = configuration.getMetadataApplication();
            if (application == null) {
                application = "JasperReports Library version "
                        + Package.getPackage("net.sf.jasperreports.engine").getImplementationVersion();
            }
            appHelper.exportProperty(PropsAppHelper.PROPERTY_APPLICATION, application);

            coreHelper.exportHeader();

            String title = configuration.getMetadataTitle();
            if (title != null) {
                coreHelper.exportProperty(PropsCoreHelper.PROPERTY_TITLE, title);
            }
            String subject = configuration.getMetadataSubject();
            if (subject != null) {
                coreHelper.exportProperty(PropsCoreHelper.PROPERTY_SUBJECT, subject);
            }
            String author = configuration.getMetadataAuthor();
            if (author != null) {
                coreHelper.exportProperty(PropsCoreHelper.PROPERTY_CREATOR, author);
            }
            String keywords = configuration.getMetadataKeywords();
            if (keywords != null) {
                coreHelper.exportProperty(PropsCoreHelper.PROPERTY_KEYWORDS, keywords);
            }

            styleHelper = new XlsxStyleHelper(jasperReportsContext, xlsxZip.getStylesEntry().getWriter(),
                    getExporterKey());

            firstPageNotSet = true;
            firstSheetName = null;
        } catch (IOException e) {
            throw new JRException(e);
        }
    }

    protected void setBackground() {
        // TODO Auto-generated method stub

    }

    //   protected void setCell(JRExporterGridCell gridCell, int colIndex, int rowIndex) 
    //   {
    //   }

    @Override
    protected void addOccupiedCell(OccupiedGridCell occupiedGridCell, int colIndex, int rowIndex) {
        //ElementGridCell elementGridCell = (ElementGridCell)occupiedGridCell.getOccupier();
        cellHelper.exportHeader(occupiedGridCell, rowIndex, colIndex, maxColumnIndex, sheetInfo);
        cellHelper.exportFooter();
    }

    @Override
    protected void setColumnWidth(int col, int width, boolean autoFit) {
        sheetHelper.exportColumn(col, width, autoFit);
    }

    @Override
    protected void setRowHeight(int rowIndex, int rowHeight, Cut yCut, XlsRowLevelInfo levelInfo)
            throws JRException {
        sheetHelper.exportRow(rowHeight, yCut, levelInfo);
    }

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

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

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

    @Override
    protected void setFreezePane(int rowIndex, int colIndex) {
        //nothing to do here
    }

    @Override
    protected void setSheetName(String sheetName) {
        /* nothing to do here; it's done in createSheet() */
    }

    @Override
    protected void setAutoFilter(String autoFilterRange) {
        sheetAutoFilter = autoFilterRange;
    }

    @Override
    protected void resetAutoFilters() {
        super.resetAutoFilters();
        sheetAutoFilter = null;
    }

    @Override
    protected void setRowLevels(XlsRowLevelInfo levelInfo, String level) {
        /* nothing to do here; it's done in setRowHeight */
    }

    protected void setScale(Integer scale) {
        /* nothing to do here; it's already done in the abstract exporter */
    }

    protected String getAnchorType(ImageAnchorTypeEnum anchorType) {
        switch (anchorType) {
        case MOVE_SIZE:
            return TWO_CELL;
        case NO_MOVE_NO_SIZE:
            return ABSOLUTE;
        case MOVE_NO_SIZE:
        default:
            return ONE_CELL;
        }
    }

    protected String getDefinedName(String name) {
        if (name != null) {
            return name.replaceAll("\\W", "");
        }
        return null;
    }

}