Package net.sf.jasperreports.engine.export

Source Code of net.sf.jasperreports.engine.export.JRHtmlExporter$ExporterContext

/*
* JasperReports - Free Java Reporting Library.
* Copyright (C) 2001 - 2009 Jaspersoft Corporation. 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/>.
*/

/*
* Contributors:
* Alex Parfenov - aparfeno@users.sourceforge.net
* Adrian Jackson - iapetus@users.sourceforge.net
* David Taylor - exodussystems@users.sourceforge.net
* Lars Kristensen - llk@users.sourceforge.net
*/

package net.sf.jasperreports.engine.export;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.font.TextAttribute;
import java.awt.geom.Dimension2D;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.text.AttributedCharacterIterator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;

import net.sf.jasperreports.crosstabs.JRCellContents;
import net.sf.jasperreports.engine.JRAbstractExporter;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExporterParameter;
import net.sf.jasperreports.engine.JRGenericPrintElement;
import net.sf.jasperreports.engine.JRImageMapRenderer;
import net.sf.jasperreports.engine.JRImageRenderer;
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.JRPrintEllipse;
import net.sf.jasperreports.engine.JRPrintFrame;
import net.sf.jasperreports.engine.JRPrintGraphicElement;
import net.sf.jasperreports.engine.JRPrintHyperlink;
import net.sf.jasperreports.engine.JRPrintHyperlinkParameter;
import net.sf.jasperreports.engine.JRPrintImage;
import net.sf.jasperreports.engine.JRPrintImageArea;
import net.sf.jasperreports.engine.JRPrintImageAreaHyperlink;
import net.sf.jasperreports.engine.JRPrintLine;
import net.sf.jasperreports.engine.JRPrintPage;
import net.sf.jasperreports.engine.JRPrintRectangle;
import net.sf.jasperreports.engine.JRPrintText;
import net.sf.jasperreports.engine.JRRenderable;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JRWrappingSvgRenderer;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.base.JRBasePrintFrame;
import net.sf.jasperreports.engine.fonts.FontFamily;
import net.sf.jasperreports.engine.fonts.FontInfo;
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.RunDirectionEnum;
import net.sf.jasperreports.engine.type.ScaleImageEnum;
import net.sf.jasperreports.engine.util.JRColorUtil;
import net.sf.jasperreports.engine.util.JRFontUtil;
import net.sf.jasperreports.engine.util.JRProperties;
import net.sf.jasperreports.engine.util.JRStringUtil;
import net.sf.jasperreports.engine.util.JRStyledText;
import net.sf.jasperreports.engine.util.JRTextAttribute;
import net.sf.jasperreports.engine.util.Pair;

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


/**
* Exports a JasperReports document to HTML format. It has character output type and exports the document to a
* grid-based layout.
*
* @author Teodor Danciu (teodord@users.sourceforge.net)
* @version $Id: JRHtmlExporter.java 4297 2011-04-18 15:23:50Z teodord $
*/
public class JRHtmlExporter extends JRAbstractExporter
{
 
  private static final Log log = LogFactory.getLog(JRHtmlExporter.class);

  private static final String HTML_EXPORTER_PROPERTIES_PREFIX = JRProperties.PROPERTY_PREFIX + "export.html.";

  /**
   * The exporter key, as used in
   * {@link GenericElementHandlerEnviroment#getHandler(net.sf.jasperreports.engine.JRGenericElementType, String)}.
   */
  public static final String HTML_EXPORTER_KEY = JRProperties.PROPERTY_PREFIX + "html";
 
  /**
   * @deprecated Replaced by  {@link JRHtmlExporterParameter#PROPERTY_FRAMES_AS_NESTED_TABLES}.
   */
  public static final String PROPERTY_FRAMES_AS_NESTED_TABLES = JRHtmlExporterParameter.PROPERTY_FRAMES_AS_NESTED_TABLES;
 
  /**
   *
   */
  public static final String PROPERTY_HTML_CLASS = HTML_EXPORTER_PROPERTIES_PREFIX + "class";

  /**
   *
   */
  public static final String PROPERTY_HTML_ID = HTML_EXPORTER_PROPERTIES_PREFIX + "id";

  /**
   * Configuration property that determines the exporter to produce accessible HTML.
   */
  public static final String PROPERTY_ACCESSIBLE = HTML_EXPORTER_PROPERTIES_PREFIX + "accessible";
 
  /**
   *
   */
  protected static final String JR_PAGE_ANCHOR_PREFIX = "JR_PAGE_ANCHOR_";

  protected static final float DEFAULT_ZOOM = 1f;

  /**
   *
   */
  protected static final String CSS_TEXT_ALIGN_LEFT = "left";
  protected static final String CSS_TEXT_ALIGN_RIGHT = "right";
  protected static final String CSS_TEXT_ALIGN_CENTER = "center";
  protected static final String CSS_TEXT_ALIGN_JUSTIFY = "justify";

  /**
   *
   */
  protected static final String HTML_VERTICAL_ALIGN_TOP = "top";
  protected static final String HTML_VERTICAL_ALIGN_MIDDLE = "middle";
  protected static final String HTML_VERTICAL_ALIGN_BOTTOM = "bottom";
 
  public static final String IMAGE_NAME_PREFIX = "img_";
  protected static final int IMAGE_NAME_PREFIX_LEGTH = IMAGE_NAME_PREFIX.length();

  protected class ExporterContext extends BaseExporterContext implements JRHtmlExporterContext
  {
    public String getExportPropertiesPrefix()
    {
      return HTML_EXPORTER_PROPERTIES_PREFIX;
    }

    public String getHyperlinkURL(JRPrintHyperlink link)
    {
      return JRHtmlExporter.this.getHyperlinkURL(link);
    }
  }

  /**
   *
   */
  protected Writer writer;
  protected JRExportProgressMonitor progressMonitor;
  protected Map rendererToImagePathMap;
  protected Map imageMaps;
  protected Map imageNameToImageDataMap;
  protected List imagesToProcess;
  protected boolean isPxImageLoaded;

  protected int reportIndex;
  protected int pageIndex;

  /**
   *
   */
  protected File imagesDir;
  protected String imagesURI;
  protected boolean isOutputImagesToDir;
  protected boolean isRemoveEmptySpace;
  protected boolean isWhitePageBackground;
  protected String encoding;
  protected String sizeUnit;
  protected float zoom = DEFAULT_ZOOM;
  protected boolean isUsingImagesToAlign;
  protected boolean isWrapBreakWord;
  protected boolean isIgnorePageMargins;
  protected boolean accessibleHtml;

  protected boolean flushOutput;

  /**
   *
   */
  protected String htmlHeader;
  protected String betweenPagesHtml;
  protected String htmlFooter;

  protected StringProvider emptyCellStringProvider;


  /**
   * @deprecated
   */
  protected Map fontMap;

  private LinkedList backcolorStack;
  private Color backcolor;

  protected JRHyperlinkTargetProducerFactory targetProducerFactory = new DefaultHyperlinkTargetProducerFactory();   

  protected boolean hyperlinkStarted;
  protected int thDepth;
 
  protected ExporterNature nature;

  protected JRHtmlExporterContext exporterContext = new ExporterContext();

  public JRHtmlExporter()
  {
    backcolorStack = new LinkedList();
    backcolor = null;
  }


  /**
   *
   */
  public void exportReport() throws JRException
  {
    progressMonitor = (JRExportProgressMonitor)parameters.get(JRExporterParameter.PROGRESS_MONITOR);

    /*   */
    setOffset();

    try
    {
      /*   */
      setExportContext();
 
      /*   */
      setInput();
     
      if (!parameters.containsKey(JRExporterParameter.FILTER))
      {
        filter = createFilter(HTML_EXPORTER_PROPERTIES_PREFIX);
      }

      /*   */
      if (!isModeBatch)
      {
        setPageRange();
      }
 
      htmlHeader = (String)parameters.get(JRHtmlExporterParameter.HTML_HEADER);
      betweenPagesHtml = (String)parameters.get(JRHtmlExporterParameter.BETWEEN_PAGES_HTML);
      htmlFooter = (String)parameters.get(JRHtmlExporterParameter.HTML_FOOTER);
 
      imagesDir = (File)parameters.get(JRHtmlExporterParameter.IMAGES_DIR);
      if (imagesDir == null)
      {
        String dir = (String)parameters.get(JRHtmlExporterParameter.IMAGES_DIR_NAME);
        if (dir != null)
        {
          imagesDir = new File(dir);
        }
      }
 
      isRemoveEmptySpace =
        getBooleanParameter(
          JRHtmlExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS,
          JRHtmlExporterParameter.PROPERTY_REMOVE_EMPTY_SPACE_BETWEEN_ROWS,
          false
          );
 
      isWhitePageBackground =
        getBooleanParameter(
          JRHtmlExporterParameter.IS_WHITE_PAGE_BACKGROUND,
          JRHtmlExporterParameter.PROPERTY_WHITE_PAGE_BACKGROUND,
          true
          );
 
      Boolean isOutputImagesToDirParameter = (Boolean)parameters.get(JRHtmlExporterParameter.IS_OUTPUT_IMAGES_TO_DIR);
      if (isOutputImagesToDirParameter != null)
      {
        isOutputImagesToDir = isOutputImagesToDirParameter.booleanValue();
      }
 
      String uri = (String)parameters.get(JRHtmlExporterParameter.IMAGES_URI);
      if (uri != null)
      {
        imagesURI = uri;
      }
 
      encoding =
        getStringParameterOrDefault(
          JRExporterParameter.CHARACTER_ENCODING,
          JRExporterParameter.PROPERTY_CHARACTER_ENCODING
          );
 
      rendererToImagePathMap = new HashMap();
      imageMaps = new HashMap();
      imagesToProcess = new ArrayList();
      isPxImageLoaded = false;
 
      //backward compatibility with the IMAGE_MAP parameter
      imageNameToImageDataMap = (Map)parameters.get(JRHtmlExporterParameter.IMAGES_MAP);
  //    if (imageNameToImageDataMap == null)
  //    {
  //      imageNameToImageDataMap = new HashMap();
  //    }
      //END - backward compatibility with the IMAGE_MAP parameter
 
      isWrapBreakWord =
        getBooleanParameter(
          JRHtmlExporterParameter.IS_WRAP_BREAK_WORD,
          JRHtmlExporterParameter.PROPERTY_WRAP_BREAK_WORD,
          false
          );
 
      sizeUnit =
        getStringParameterOrDefault(
          JRHtmlExporterParameter.SIZE_UNIT,
          JRHtmlExporterParameter.PROPERTY_SIZE_UNIT
          );
 
      Float zoomRatio = (Float)parameters.get(JRHtmlExporterParameter.ZOOM_RATIO);
      if (zoomRatio != null)
      {
        zoom = zoomRatio.floatValue();
        if (zoom <= 0)
        {
          throw new JRException("Invalid zoom ratio : " + zoom);
        }
      }
      else
      {
        zoom = DEFAULT_ZOOM;
      }
 
      isUsingImagesToAlign =
        getBooleanParameter(
          JRHtmlExporterParameter.IS_USING_IMAGES_TO_ALIGN,
          JRHtmlExporterParameter.PROPERTY_USING_IMAGES_TO_ALIGN,
          true
          );
   
      if (isUsingImagesToAlign)
      {
        emptyCellStringProvider =
          new StringProvider()
          {
            public String getStringForCollapsedTD(Object value, int width, int height)
            {
              return "><img alt=\"\" src=\"" + value + "px\" style=\"width: " + toSizeUnit(width) + "; height: " + toSizeUnit(height) + ";\"/>";
            }
            public String getStringForEmptyTD(Object value)
            {
              return "<img alt=\"\" src=\"" + value + "px\" border=\"0\"/>";
            }
           
            public String getReportTableStyle()
            {
              return null;
            }
          };

        loadPxImage();
      }
      else
      {
        emptyCellStringProvider =
          new StringProvider()
          {
            public String getStringForCollapsedTD(Object value, int width, int height)
            {
              return " style=\"width: " + toSizeUnit(width) + "; height: " + toSizeUnit(height) + ";\">";
            }
            public String getStringForEmptyTD(Object value)
            {
              return "";
            }
           
            public String getReportTableStyle()
            {
              // required for lines and rectangles, but doesn't work in IE
              // border-collapse: collapse seems to take care of this though
              return "empty-cells: show";
            }
          };
      }
     
      isIgnorePageMargins =
        getBooleanParameter(
          JRExporterParameter.IGNORE_PAGE_MARGINS,
          JRExporterParameter.PROPERTY_IGNORE_PAGE_MARGINS,
          false
          );
     
      accessibleHtml =
        JRProperties.getBooleanProperty(
          jasperPrint,
          PROPERTY_ACCESSIBLE,
          false
          );
     
      fontMap = (Map) parameters.get(JRExporterParameter.FONT_MAP);
           
      setHyperlinkProducerFactory();

      //FIXMENOW check all exporter properties that are supposed to work at report level
      boolean deepGrid =
        !getBooleanParameter(
          JRHtmlExporterParameter.FRAMES_AS_NESTED_TABLES,
          JRHtmlExporterParameter.PROPERTY_FRAMES_AS_NESTED_TABLES,
          true
          );
     
      nature = new JRHtmlExporterNature(filter, deepGrid, isIgnorePageMargins);
 
      flushOutput = getBooleanParameter(JRHtmlExporterParameter.FLUSH_OUTPUT,
          JRHtmlExporterParameter.PROPERTY_FLUSH_OUTPUT,
          true);
     
      StringBuffer sb = (StringBuffer)parameters.get(JRExporterParameter.OUTPUT_STRING_BUFFER);
      if (sb != null)
      {
        try
        {
          writer = new StringWriter();
          exportReportToWriter();
          sb.append(writer.toString());
        }
        catch (IOException e)
        {
          throw new JRException("Error writing to StringBuffer writer : " + jasperPrint.getName(), e);
        }
        finally
        {
          if (writer != null)
          {
            try
            {
              writer.close();
            }
            catch(IOException e)
            {
            }
          }
        }
      }
      else
      {
        writer = (Writer)parameters.get(JRExporterParameter.OUTPUT_WRITER);
        if (writer != null)
        {
          try
          {
            exportReportToWriter();
          }
          catch (IOException e)
          {
            throw new JRException("Error writing to writer : " + jasperPrint.getName(), e);
          }
        }
        else
        {
          OutputStream os = (OutputStream)parameters.get(JRExporterParameter.OUTPUT_STREAM);
          if (os != null)
          {
            try
            {
              writer = new OutputStreamWriter(os, encoding);
              exportReportToWriter();
            }
            catch (IOException e)
            {
              throw new JRException("Error writing to OutputStream writer : " + jasperPrint.getName(), e);
            }
          }
          else
          {
            File destFile = (File)parameters.get(JRExporterParameter.OUTPUT_FILE);
            if (destFile == null)
            {
              String fileName = (String)parameters.get(JRExporterParameter.OUTPUT_FILE_NAME);
              if (fileName != null)
              {
                destFile = new File(fileName);
              }
              else
              {
                throw new JRException("No output specified for the exporter.");
              }
            }
 
            try
            {
              os = new FileOutputStream(destFile);
              writer = new OutputStreamWriter(os, encoding);
            }
            catch (IOException e)
            {
              throw new JRException("Error creating to file writer : " + jasperPrint.getName(), e);
            }
 
            if (imagesDir == null)
            {
              imagesDir = new File(destFile.getParent(), destFile.getName() + "_files");
            }
 
            if (isOutputImagesToDirParameter == null)
            {
              isOutputImagesToDir = true;
            }
 
            if (imagesURI == null)
            {
              imagesURI = imagesDir.getName() + "/";
            }
 
            try
            {
              exportReportToWriter();
            }
            catch (IOException e)
            {
              throw new JRException("Error writing to file writer : " + jasperPrint.getName(), e);
            }
            finally
            {
              if (writer != null)
              {
                try
                {
                  writer.close();
                }
                catch(IOException e)
                {
                }
              }
            }
          }
        }
      }
 
      if (isOutputImagesToDir)
      {
        if (imagesDir == null)
        {
          throw new JRException("The images directory was not specified for the exporter.");
        }
 
        if (isPxImageLoaded || (imagesToProcess != null && imagesToProcess.size() > 0))
        {
          if (!imagesDir.exists())
          {
            imagesDir.mkdir();
          }
 
          if (isPxImageLoaded)
          {
            JRRenderable pxRenderer =
              JRImageRenderer.getInstance("net/sf/jasperreports/engine/images/pixel.GIF");
            byte[] imageData = pxRenderer.getImageData();
 
            File imageFile = new File(imagesDir, "px");
            FileOutputStream fos = null;
 
            try
            {
              fos = new FileOutputStream(imageFile);
              fos.write(imageData, 0, imageData.length);
            }
            catch (IOException e)
            {
              throw new JRException("Error writing to image file : " + imageFile, e);
            }
            finally
            {
              if (fos != null)
              {
                try
                {
                  fos.close();
                }
                catch(IOException e)
                {
                }
              }
            }
          }
 
          for(Iterator it = imagesToProcess.iterator(); it.hasNext();)
          {
            JRPrintElementIndex imageIndex = (JRPrintElementIndex)it.next();
 
            JRPrintImage image = getImage(jasperPrintList, imageIndex);
            JRRenderable renderer = image.getRenderer();
            if (renderer.getType() == JRRenderable.TYPE_SVG)
            {
              renderer =
                new JRWrappingSvgRenderer(
                  renderer,
                  new Dimension(image.getWidth(), image.getHeight()),
                  ModeEnum.OPAQUE == image.getModeValue() ? image.getBackcolor() : null
                  );
            }
 
            byte[] imageData = renderer.getImageData();
 
            File imageFile = new File(imagesDir, getImageName(imageIndex));
            FileOutputStream fos = null;
 
            try
            {
              fos = new FileOutputStream(imageFile);
              fos.write(imageData, 0, imageData.length);
            }
            catch (IOException e)
            {
              throw new JRException("Error writing to image file : " + imageFile, e);
            }
            finally
            {
              if (fos != null)
              {
                try
                {
                  fos.close();
                }
                catch(IOException e)
                {
                }
              }
            }
          }
        }
      }
    }
    finally
    {
      resetExportContext();
    }
  }


  public static JRPrintImage getImage(List jasperPrintList, String imageName)
  {
    return getImage(jasperPrintList, getPrintElementIndex(imageName));
  }


  public static JRPrintImage getImage(List jasperPrintList, JRPrintElementIndex imageIndex)
  {
    JasperPrint report = (JasperPrint)jasperPrintList.get(imageIndex.getReportIndex());
    JRPrintPage page = (JRPrintPage)report.getPages().get(imageIndex.getPageIndex());

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

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

    return (JRPrintImage) element;
  }


  /**
   *
   */
  protected void exportReportToWriter() throws JRException, IOException
  {
    if (htmlHeader == null)
    {
      // no doctype because of bug 1430880
//      writer.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n");
//      writer.write("<html xmlns=\"http://www.w3.org/1999/xhtml\">\n");
      writer.write("<html>\n");
      writer.write("<head>\n");
      writer.write("  <title></title>\n");
      writer.write("  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=" + encoding + "\"/>\n");
      writer.write("  <style type=\"text/css\">\n");
      writer.write("    a {text-decoration: none}\n");
      writer.write("  </style>\n");
      writer.write("</head>\n");
      writer.write("<body text=\"#000000\" link=\"#000000\" alink=\"#000000\" vlink=\"#000000\">\n");
      writer.write("<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\">\n");
      writer.write("<tr><td width=\"50%\">&nbsp;</td><td align=\"center\">\n");
      writer.write("\n");
    }
    else
    {
      writer.write(htmlHeader);
    }

    for(reportIndex = 0; reportIndex < jasperPrintList.size(); reportIndex++)
    {
      setJasperPrint((JasperPrint)jasperPrintList.get(reportIndex));

      List pages = jasperPrint.getPages();
      if (pages != null && pages.size() > 0)
      {
        if (isModeBatch)
        {
          startPageIndex = 0;
          endPageIndex = pages.size() - 1;
        }

        JRPrintPage page = null;
        for(pageIndex = startPageIndex; pageIndex <= endPageIndex; pageIndex++)
        {
          if (Thread.interrupted())
          {
            throw new JRException("Current thread interrupted.");
          }

          page = (JRPrintPage)pages.get(pageIndex);

          writer.write("<a name=\"" + JR_PAGE_ANCHOR_PREFIX + reportIndex + "_" + (pageIndex + 1) + "\"></a>\n");

          /*   */
          exportPage(page);

          if (reportIndex < jasperPrintList.size() - 1 || pageIndex < endPageIndex)
          {
            if (betweenPagesHtml == null)
            {
              writer.write("<br/>\n<br/>\n");
            }
            else
            {
              writer.write(betweenPagesHtml);
            }
          }

          writer.write("\n");
        }
      }
    }

    if (htmlFooter == null)
    {
      writer.write("</td><td width=\"50%\">&nbsp;</td></tr>\n");
      writer.write("</table>\n");
      writer.write("</body>\n");
      writer.write("</html>\n");
    }
    else
    {
      writer.write(htmlFooter);
    }

    if (flushOutput)
    {
      writer.flush();
    }
  }


  /**
   *
   */
  protected void exportPage(JRPrintPage page) throws JRException, IOException
  {
    List elements = null;
   
    if (accessibleHtml)
    {
      JRBasePrintFrame frame = new JRBasePrintFrame(jasperPrint.getDefaultStyleProvider());

      new JRHtmlExporterHelper(jasperPrint).createNestedFrames(page.getElements().listIterator(), frame);
     
      elements = frame.getElements();
    }
    else
    {
      elements = page.getElements();
    }
   
    JRGridLayout layout =
      new JRGridLayout(
        nature,
        elements,
        jasperPrint.getPageWidth(),
        jasperPrint.getPageHeight(),
        globalOffsetX,
        globalOffsetY,
        null //address
        );

    exportGrid(layout, isWhitePageBackground);

    if (progressMonitor != null)
    {
      progressMonitor.afterPageExport();
    }
  }
 

  /**
   *
   */
  protected void exportGrid(JRGridLayout gridLayout, boolean whitePageBackground) throws IOException, JRException
  {
    CutsInfo xCuts = gridLayout.getXCuts();
    JRExporterGridCell[][] grid = gridLayout.getGrid();

    String tableStyle = "width: " + toSizeUnit(gridLayout.getWidth()) + "; border-collapse: collapse";
    String additionalTableStyle = emptyCellStringProvider.getReportTableStyle();
    if (additionalTableStyle != null)
    {
      tableStyle += "; " + additionalTableStyle;
    }
   
    writer.write("<table style=\"" + tableStyle + "\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\"");
    if (whitePageBackground)
    {
      writer.write(" bgcolor=\"white\"");
    }
    writer.write(">\n");

    if (whitePageBackground)
    {
      setBackcolor(Color.white);
    }

    writer.write("<tr>\n");
    int width = 0;
    for(int i = 1; i < xCuts.size(); i++)
    {
      width = xCuts.getCut(i) - xCuts.getCut(i - 1);
      writer.write("  <td" + emptyCellStringProvider.getStringForCollapsedTD(imagesURI, width, 1) + "</td>\n");
    }
    writer.write("</tr>\n");
   
    thDepth = 0;
    for(int y = 0; y < grid.length; y++)
    {
      if (gridLayout.getYCuts().isCutSpanned(y) || !isRemoveEmptySpace)
      {
        JRExporterGridCell[] gridRow = grid[y];
       
        int rowHeight = JRGridLayout.getRowHeight(gridRow);
       
        boolean hasEmptyCell = hasEmptyCell(gridRow);
       
        writer.write("<tr valign=\"top\"");
        if (!hasEmptyCell)
        {
          writer.write(" style=\"height:" + toSizeUnit(rowHeight) + "\"");
        }
        writer.write(">\n");

        for(int x = 0; x < gridRow.length; x++)
        {
          JRExporterGridCell gridCell = gridRow[x];
          if(gridCell.getWrapper() == null)
          {
            writeEmptyCell(gridCell, rowHeight);           
          }
          else
          {
           
            JRPrintElement element = gridCell.getWrapper().getElement();
           
            String thTag = null;

            if (
              element != null
              && element.hasProperties()
              )
            {
              thTag = element.getPropertiesMap().getProperty(JRPdfExporterTagHelper.PROPERTY_TAG_TH);
            }
           
            if (thTag != null && (JRPdfExporterTagHelper.TAG_START.equals(thTag) || JRPdfExporterTagHelper.TAG_FULL.equals(thTag)))
            {
              thDepth++;
            }

            if (element instanceof JRPrintLine)
            {
              exportLine((JRPrintLine)element, gridCell);
            }
            else if (element instanceof JRPrintRectangle)
            {
              exportRectangle((JRPrintRectangle)element, gridCell);
            }
            else if (element instanceof JRPrintEllipse)
            {
              exportRectangle((JRPrintEllipse)element, gridCell);
            }
            else if (element instanceof JRPrintImage)
            {
              exportImage((JRPrintImage)element, gridCell);
            }
            else if (element instanceof JRPrintText)
            {
              exportText((JRPrintText)element, gridCell);
            }
            else if (element instanceof JRPrintFrame)
            {
              exportFrame((JRPrintFrame) element, gridCell);
            }
            else if (element instanceof JRGenericPrintElement)
            {
              exportGenericElement((JRGenericPrintElement) element,
                  gridCell, rowHeight);
            }

            if (thTag != null && (JRPdfExporterTagHelper.TAG_END.equals(thTag) || JRPdfExporterTagHelper.TAG_FULL.equals(thTag)))
            {
              thDepth--;
            }
          }

          x += gridCell.getColSpan() - 1;
        }

        writer.write("</tr>\n");
      }
    }

    if (whitePageBackground)
    {
      restoreBackcolor();
    }

    writer.write("</table>\n");
  }


  private boolean hasEmptyCell(JRExporterGridCell[] gridRow)
  {
    if (gridRow[0].getWrapper() == null) // quick exit
    {
      return true;
    }
   
    boolean hasEmptyCell = false;
    for(int x = 1; x < gridRow.length; x++)
    {
      if (gridRow[x].getWrapper() == null)
      {
        hasEmptyCell = true;
        break;
      }
    }

    return hasEmptyCell;
  }


  protected void writeEmptyCell(JRExporterGridCell cell, int rowHeight) throws IOException
  {
    String cellTag = getCellTag(cell);
   
    writer.write("  <" + cellTag);
    if (cell.getColSpan() > 1)
    {
      writer.write(" colspan=\"" + cell.getColSpan() + "\"");
    }

    StringBuffer styleBuffer = new StringBuffer();
    appendBackcolorStyle(cell, styleBuffer);
    appendBorderStyle(cell.getBox(), styleBuffer);

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

    writer.write(emptyCellStringProvider.getStringForCollapsedTD(imagesURI, cell.getWidth(), rowHeight));
    writer.write("</" + cellTag + ">\n");
  }


  /**
   *
   */
  protected void exportLine(JRPrintLine line, JRExporterGridCell gridCell) throws IOException
  {
    writeCellStart(gridCell);

    StringBuffer styleBuffer = new StringBuffer();

    appendBackcolorStyle(gridCell, styleBuffer);
   
    String side = null;
    float ratio = line.getWidth() / line.getHeight();
    if (ratio > 1)
    {
      if (line.getDirectionValue() == LineDirectionEnum.TOP_DOWN)
      {
        side = "top";
      }
      else
      {
        side = "bottom";
      }
    }
    else
    {
      if (line.getDirectionValue() == LineDirectionEnum.TOP_DOWN)
      {
        side = "left";
      }
      else
      {
        side = "right";
      }
    }

    appendPen(
      styleBuffer,
      line.getLinePen(),
      side
      );

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

    writer.write(">");

    writer.write(emptyCellStringProvider.getStringForEmptyTD(imagesURI));

    writeCellEnd(gridCell);
  }


  /**
   *
   */
  protected void writeCellStart(JRExporterGridCell gridCell) throws IOException
  {
    writer.write("  <" + getCellTag(gridCell));
    if (gridCell.getColSpan() > 1)
    {
      writer.write(" colspan=\"" + gridCell.getColSpan() +"\"");
    }
    if (gridCell.getRowSpan() > 1)
    {
      writer.write(" rowspan=\"" + gridCell.getRowSpan() + "\"");
    }
    if (gridCell.getWrapper() != null)
    {
      JRPrintElement element = gridCell.getWrapper().getElement();
      if (element != null)
      {
        String id = JRProperties.getProperty(element, PROPERTY_HTML_ID);
        if (id != null)
        {
          writer.write(" id=\"" + id +"\"");
        }
        String clazz = JRProperties.getProperty(element, PROPERTY_HTML_CLASS);
        if (clazz != null)
        {
          writer.write(" class=\"" + clazz +"\"");
        }
      }
    }
  }


  /**
   *
   */
  protected void writeCellEnd(JRExporterGridCell gridCell) throws IOException
  {
    writer.write("</" + getCellTag(gridCell) + ">\n");
  }


  /**
   *
   */
  protected String getCellTag(JRExporterGridCell gridCell)
  {
    if (accessibleHtml)
    {
      if (thDepth > 0)
      {
        return "th"; //FIXMEHTML th tags have center alignment by default
      }
      else
      {
        ElementWrapper wrapper = gridCell.getWrapper();

        OccupiedGridCell occupiedCell = gridCell instanceof OccupiedGridCell ? (OccupiedGridCell)gridCell : null;
        if (occupiedCell != null)
        {
          wrapper = occupiedCell.getOccupier().getWrapper();
        }
       
        if (wrapper != null)
        {
          String cellContentsType = wrapper.getProperty(JRCellContents.PROPERTY_TYPE);
          if (
            JRCellContents.TYPE_CROSSTAB_HEADER.equals(cellContentsType)
            || JRCellContents.TYPE_COLUMN_HEADER.equals(cellContentsType)
            || JRCellContents.TYPE_ROW_HEADER.equals(cellContentsType)
            )
          {
            return "th";
          }
        }
      }
    }
   
    return "td";
  }


  /**
   *
   */
  protected void exportRectangle(JRPrintGraphicElement element, JRExporterGridCell gridCell) throws IOException
  {
    writeCellStart(gridCell);

    StringBuffer styleBuffer = new StringBuffer();

    appendBackcolorStyle(gridCell, styleBuffer);
   
    appendPen(
      styleBuffer,
      element.getLinePen(),
      null
      );

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

    writer.write(">");

    writer.write(emptyCellStringProvider.getStringForEmptyTD(imagesURI));

    writeCellEnd(gridCell);
  }


  /**
   *
   */
  protected void exportStyledText(JRStyledText styledText, Locale locale) throws IOException
  {
    exportStyledText(styledText, null, locale);
  }
 

  /**
   *
   */
  protected void exportStyledText(JRStyledText styledText, String tooltip, Locale locale) throws IOException
  {
    String text = styledText.getText();

    int runLimit = 0;

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

    boolean first = true;
    boolean startedSpan = false;
    while(runLimit < styledText.length() && (runLimit = iterator.getRunLimit()) <= styledText.length())
    {
      //if there are several text runs, write the tooltip into a parent <span>
      if (first && runLimit < styledText.length() && tooltip != null)
      {
        startedSpan = true;
        writer.write("<span title=\"");
        writer.write(JRStringUtil.xmlEncode(tooltip));
        writer.write("\">");
        //reset the tooltip so that inner <span>s to not use it
        tooltip = null;
      }
      first = false;
     
      exportStyledTextRun(
        iterator.getAttributes(),
        text.substring(iterator.getIndex(), runLimit),
        tooltip,
        locale
        );

      iterator.setIndex(runLimit);
    }
   
    if (startedSpan)
    {
      writer.write("</span>");
    }
  }


  /**
   *
   */
  protected void exportStyledTextRun(Map attributes, String text, Locale locale) throws IOException
  {
    exportStyledTextRun(attributes, text, null, locale);
  }

 
  /**
   *
   */
  protected void exportStyledTextRun(
    Map attributes,
    String text,
    String tooltip,
    Locale locale
    ) throws IOException
  {
    String fontFamilyAttr = (String)attributes.get(TextAttribute.FAMILY);
    String fontFamily = fontFamilyAttr;
    if (fontMap != null && fontMap.containsKey(fontFamilyAttr))
    {
      fontFamily = (String) fontMap.get(fontFamilyAttr);
    }
    else
    {
      FontInfo fontInfo = JRFontUtil.getFontInfo(fontFamilyAttr, locale);
      if (fontInfo != null)
      {
        //fontName found in font extensions
        FontFamily family = fontInfo.getFontFamily();
        String exportFont = family.getExportFont(getExporterKey());
        if (exportFont != null)
        {
          fontFamily = exportFont;
        }
      }
    }
   
    boolean localHyperlink = false;
    JRPrintHyperlink hyperlink = (JRPrintHyperlink)attributes.get(JRTextAttribute.HYPERLINK);
    if (!hyperlinkStarted && hyperlink != null)
    {
      startHyperlink(hyperlink);
      localHyperlink = true;
    }
   
    writer.write("<span style=\"font-family: ");
    writer.write(fontFamily);
    writer.write("; ");

    Color forecolor = (Color)attributes.get(TextAttribute.FOREGROUND);
    if (!hyperlinkStarted || !Color.black.equals(forecolor))
    {
      writer.write("color: #");
      writer.write(JRColorUtil.getColorHexa(forecolor));
      writer.write("; ");
    }

    Color runBackcolor = (Color)attributes.get(TextAttribute.BACKGROUND);
    if (runBackcolor != null)
    {
      writer.write("background-color: #");
      writer.write(JRColorUtil.getColorHexa(runBackcolor));
      writer.write("; ");
    }

    writer.write("font-size: ");
    writer.write(toSizeUnit(((Float)attributes.get(TextAttribute.SIZE)).intValue()));
    writer.write(";");

    /*
    if (!horizontalAlignment.equals(CSS_TEXT_ALIGN_LEFT))
    {
      writer.write(" text-align: ");
      writer.write(horizontalAlignment);
      writer.write(";");
    }
    */

    if (TextAttribute.WEIGHT_BOLD.equals(attributes.get(TextAttribute.WEIGHT)))
    {
      writer.write(" font-weight: bold;");
    }
    if (TextAttribute.POSTURE_OBLIQUE.equals(attributes.get(TextAttribute.POSTURE)))
    {
      writer.write(" font-style: italic;");
    }
    if (TextAttribute.UNDERLINE_ON.equals(attributes.get(TextAttribute.UNDERLINE)))
    {
      writer.write(" text-decoration: underline;");
    }
    if (TextAttribute.STRIKETHROUGH_ON.equals(attributes.get(TextAttribute.STRIKETHROUGH)))
    {
      writer.write(" text-decoration: line-through;");
    }

    if (TextAttribute.SUPERSCRIPT_SUPER.equals(attributes.get(TextAttribute.SUPERSCRIPT)))
    {
      writer.write(" vertical-align: super;");
    }
    else if (TextAttribute.SUPERSCRIPT_SUB.equals(attributes.get(TextAttribute.SUPERSCRIPT)))
    {
      writer.write(" vertical-align: sub;");
    }
   
    writer.write("\"");

    if (tooltip != null)
    {
      writer.write(" title=\"");
      writer.write(JRStringUtil.xmlEncode(tooltip));
      writer.write("\"");
    }
   
    writer.write(">");

    writer.write(
      JRStringUtil.htmlEncode(text)
      );

    writer.write("</span>");
   
    if (localHyperlink)
    {
      endHyperlink();
    }
  }


  /**
   *
   */
  protected void exportText(JRPrintText text, JRExporterGridCell gridCell) throws IOException
  {
    JRStyledText styledText = getStyledText(text);

    int textLength = 0;

    if (styledText != null)
    {
      textLength = styledText.length();
    }

    writeCellStart(gridCell);//FIXME why dealing with cell style if no text to print (textLength == 0)?

    if (text.getRunDirectionValue() == RunDirectionEnum.RTL)
    {
      writer.write(" dir=\"rtl\"");
    }

    StringBuffer styleBuffer = new StringBuffer();

    String verticalAlignment = HTML_VERTICAL_ALIGN_TOP;

    switch (text.getVerticalAlignmentValue())
    {
      case BOTTOM :
      {
        verticalAlignment = HTML_VERTICAL_ALIGN_BOTTOM;
        break;
      }
      case MIDDLE :
      {
        verticalAlignment = HTML_VERTICAL_ALIGN_MIDDLE;
        break;
      }
      case TOP :
      default :
      {
        verticalAlignment = HTML_VERTICAL_ALIGN_TOP;
      }
    }

    if (!verticalAlignment.equals(HTML_VERTICAL_ALIGN_TOP))
    {
      styleBuffer.append(" vertical-align: ");
      styleBuffer.append(verticalAlignment);
      styleBuffer.append(";");
    }

    appendBackcolorStyle(gridCell, styleBuffer);
    appendBorderStyle(gridCell.getBox(), styleBuffer);
    appendPaddingStyle(text.getLineBox(), styleBuffer);

    String horizontalAlignment = CSS_TEXT_ALIGN_LEFT;

    if (textLength > 0)
    {
      switch (text.getHorizontalAlignmentValue())
      {
        case RIGHT :
        {
          horizontalAlignment = CSS_TEXT_ALIGN_RIGHT;
          break;
        }
        case CENTER :
        {
          horizontalAlignment = CSS_TEXT_ALIGN_CENTER;
          break;
        }
        case JUSTIFIED :
        {
          horizontalAlignment = CSS_TEXT_ALIGN_JUSTIFY;
          break;
        }
        case LEFT :
        default :
        {
          horizontalAlignment = CSS_TEXT_ALIGN_LEFT;
        }
      }

      if (
        (text.getRunDirectionValue() == RunDirectionEnum.LTR
         && !horizontalAlignment.equals(CSS_TEXT_ALIGN_LEFT))
        || (text.getRunDirectionValue() == RunDirectionEnum.RTL
          && !horizontalAlignment.equals(CSS_TEXT_ALIGN_RIGHT))
        )
      {
        styleBuffer.append("text-align: ");
        styleBuffer.append(horizontalAlignment);
        styleBuffer.append(";");
      }
    }

    if (isWrapBreakWord)
    {
      styleBuffer.append("width: " + toSizeUnit(gridCell.getWidth()) + "; ");
      styleBuffer.append("word-wrap: break-word; ");
    }
   
    if (text.getLineBreakOffsets() != null)
    {
      //if we have line breaks saved in the text, set nowrap so that
      //the text only wraps at the explicit positions
      styleBuffer.append("white-space: nowrap; ");
    }
   
    if (styleBuffer.length() > 0)
    {
      writer.write(" style=\"");
      writer.write(styleBuffer.toString());
      writer.write("\"");
    }
   
    writer.write(">");
    writer.write("<p style=\"overflow: hidden; ");

    switch (text.getParagraph().getLineSpacing())
    {
      case SINGLE:
      default:
      {
        writer.write("line-height: 1.0; ");
        break;
      }
      case ONE_AND_HALF:
      {
        writer.write("line-height: 1.5; ");
        break;
      }
      case DOUBLE:
      {
        writer.write("line-height: 2.0; ");
        break;
      }
      case PROPORTIONAL:
      {
        writer.write("line-height: " + (int)(100 * text.getParagraph().getLineSpacingSize().floatValue()) + "%; ");
        break;
      }
      case AT_LEAST:
      case FIXED:
      {
        writer.write("line-height: " + text.getParagraph().getLineSpacingSize().floatValue() + "px; ");
        break;
      }
    }

    writer.write("text-indent: " + text.getParagraph().getFirstLineIndent().intValue() + "px; ");
//    writer.write("margin-left: " + text.getParagraph().getLeftIndent().intValue() + "px; ");
//    writer.write("margin-right: " + text.getParagraph().getRightIndent().intValue() + "px; ");
//    writer.write("margin-top: " + text.getParagraph().getSpacingBefore().intValue() + "px; ");
//    writer.write("margin-bottom: " + text.getParagraph().getSpacingAfter().intValue() + "px; ");
    writer.write("\">");

    if (text.getAnchorName() != null)
    {
      writer.write("<a name=\"");
      writer.write(text.getAnchorName());
      writer.write("\"/>");
    }

    startHyperlink(text);

    if (textLength > 0)
    {
      //only use text tooltip when no hyperlink present
      String textTooltip = hyperlinkStarted ? null : text.getHyperlinkTooltip();
      exportStyledText(styledText, textTooltip, getTextLocale(text));
    }
    else
    {
      writer.write(emptyCellStringProvider.getStringForEmptyTD(imagesURI));
    }

    endHyperlink();

    writer.write("</p>");

    writeCellEnd(gridCell);
  }


  protected boolean startHyperlink(JRPrintHyperlink link) throws IOException
  {
    String href = getHyperlinkURL(link);

    if (href != null)
    {
      writer.write("<a href=\"");
      writer.write(href);
      writer.write("\"");

      String target = getHyperlinkTarget(link);
      if (target != null)
      {
        writer.write(" target=\"");
        writer.write(target);
        writer.write("\"");
      }

      if (link.getHyperlinkTooltip() != null)
      {
        writer.write(" title=\"");
        writer.write(JRStringUtil.xmlEncode(link.getHyperlinkTooltip()));
        writer.write("\"");
      }
     
      writer.write(">");
    }
   
    hyperlinkStarted = href != null;
   
    return hyperlinkStarted;
  }


  protected String getHyperlinkTarget(JRPrintHyperlink link)
  {
    String target = null;
    JRHyperlinkTargetProducer producer = targetProducerFactory.getHyperlinkTargetProducer(link.getLinkTarget());   
    if (producer == null)
    {
      switch(link.getHyperlinkTargetValue())
      {
        case BLANK :
        {
          target = "_blank";//FIXME make reverse for html markup hyperlinks
          break;
        }
        case PARENT :
        {
          target = "_parent";
          break;
        }
        case TOP :
        {
          target = "_top";
          break;
        }
        case CUSTOM :
        {
          boolean paramFound = false;
          List parameters = link.getHyperlinkParameters() == null ? null : link.getHyperlinkParameters().getParameters();
          if (parameters != null)
          {
            for(Iterator it = parameters.iterator(); it.hasNext();)
            {
              JRPrintHyperlinkParameter parameter = (JRPrintHyperlinkParameter)it.next();
              if (link.getLinkTarget().equals(parameter.getName()))
              {
                target = parameter.getValue() == null ? null : parameter.getValue().toString();
                paramFound = true;
                break;
              }
            }
          }
          if (!paramFound)
          {
            target = link.getLinkTarget();
          }
          break;
        }
        case SELF :
        default :
        {
        }
      }
    }
    else
    {
      target = producer.getHyperlinkTarget(link);
    }

    return target;
  }


  protected String getHyperlinkURL(JRPrintHyperlink link)
  {
    String href = null;
    JRHyperlinkProducer customHandler = getCustomHandler(link);   
    if (customHandler == null)
    {
      switch(link.getHyperlinkTypeValue())
      {
        case REFERENCE :
        {
          if (link.getHyperlinkReference() != null)
          {
            href = link.getHyperlinkReference();
          }
          break;
        }
        case LOCAL_ANCHOR :
        {
          if (link.getHyperlinkAnchor() != null)
          {
            href = "#" + link.getHyperlinkAnchor();
          }
          break;
        }
        case LOCAL_PAGE :
        {
          if (link.getHyperlinkPage() != null)
          {
            href = "#" + JR_PAGE_ANCHOR_PREFIX + reportIndex + "_" + link.getHyperlinkPage().toString();
          }
          break;
        }
        case REMOTE_ANCHOR :
        {
          if (
            link.getHyperlinkReference() != null &&
            link.getHyperlinkAnchor() != null
            )
          {
            href = link.getHyperlinkReference() + "#" + 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() throws IOException
  {
    if (hyperlinkStarted)
    {
      writer.write("</a>");
    }
    hyperlinkStarted = false;
  }


  protected boolean appendBorderStyle(JRLineBox box, StringBuffer styleBuffer)
  {
    boolean addedToStyle = false;
   
    if (box != null)
    {
      addedToStyle |= appendPen(
        styleBuffer,
        box.getTopPen(),
        "top"
        );
      addedToStyle |= appendPen(
        styleBuffer,
        box.getLeftPen(),
        "left"
        );
      addedToStyle |= appendPen(
        styleBuffer,
        box.getBottomPen(),
        "bottom"
        );
      addedToStyle |= appendPen(
        styleBuffer,
        box.getRightPen(),
        "right"
        );
    }
   
    return addedToStyle;
  }


  protected boolean appendPaddingStyle(JRLineBox box, StringBuffer styleBuffer)
  {
    boolean addedToStyle = false;
   
    if (box != null)
    {
      addedToStyle |= appendPadding(
        styleBuffer,
        box.getTopPadding(),
        "top"
        );
      addedToStyle |= appendPadding(
        styleBuffer,
        box.getLeftPadding(),
        "left"
        );
      addedToStyle |= appendPadding(
        styleBuffer,
        box.getBottomPadding(),
        "bottom"
        );
      addedToStyle |= appendPadding(
        styleBuffer,
        box.getRightPadding(),
        "right"
        );
    }
   
    return addedToStyle;
  }


  protected Color appendBackcolorStyle(JRExporterGridCell gridCell, StringBuffer styleBuffer)
  {
    Color cellBackcolor = gridCell.getCellBackcolor();
    if (cellBackcolor != null && (backcolor == null || cellBackcolor.getRGB() != backcolor.getRGB()))
    {
      styleBuffer.append("background-color: #");
      styleBuffer.append(JRColorUtil.getColorHexa(cellBackcolor));
      styleBuffer.append("; ");

      return cellBackcolor;
    }

    return null;
  }


  /**
   *
   */
  protected void exportImage(JRPrintImage image, JRExporterGridCell gridCell) throws JRException, IOException
  {
    writeCellStart(gridCell);

    StringBuffer styleBuffer = new StringBuffer();

    String horizontalAlignment = CSS_TEXT_ALIGN_LEFT;

    switch (image.getHorizontalAlignmentValue())
    {
      case RIGHT :
      {
        horizontalAlignment = CSS_TEXT_ALIGN_RIGHT;
        break;
      }
      case CENTER :
      {
        horizontalAlignment = CSS_TEXT_ALIGN_CENTER;
        break;
      }
      case LEFT :
      default :
      {
        horizontalAlignment = CSS_TEXT_ALIGN_LEFT;
      }
    }

    if (!horizontalAlignment.equals(CSS_TEXT_ALIGN_LEFT))
    {
      styleBuffer.append("text-align: ");
      styleBuffer.append(horizontalAlignment);
      styleBuffer.append(";");
    }

    String verticalAlignment = HTML_VERTICAL_ALIGN_TOP;

    switch (image.getVerticalAlignmentValue())
    {
      case BOTTOM :
      {
        verticalAlignment = HTML_VERTICAL_ALIGN_BOTTOM;
        break;
      }
      case MIDDLE :
      {
        verticalAlignment = HTML_VERTICAL_ALIGN_MIDDLE;
        break;
      }
      case TOP :
      default :
      {
        verticalAlignment = HTML_VERTICAL_ALIGN_TOP;
      }
    }

    if (!verticalAlignment.equals(HTML_VERTICAL_ALIGN_TOP))
    {
      styleBuffer.append(" vertical-align: ");
      styleBuffer.append(verticalAlignment);
      styleBuffer.append(";");
    }

    appendBackcolorStyle(gridCell, styleBuffer);
   
    boolean addedToStyle = appendBorderStyle(gridCell.getBox(), styleBuffer);
    if (!addedToStyle)
    {
      appendPen(
        styleBuffer,
        image.getLinePen(),
        null
        );
    }

    appendPaddingStyle(image.getLineBox(), styleBuffer);

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

    writer.write(">");

    if (image.getAnchorName() != null)
    {
      writer.write("<a name=\"");
      writer.write(image.getAnchorName());
      writer.write("\"/>");
    }
   
    JRRenderable renderer = image.getRenderer();
    JRRenderable originalRenderer = renderer;
    boolean imageMapRenderer = renderer != null
        && renderer instanceof JRImageMapRenderer
        && ((JRImageMapRenderer) renderer).hasImageAreaHyperlinks();

    boolean hasHyperlinks = false;

    if(renderer != null || isUsingImagesToAlign)
    {
      if (imageMapRenderer)
      {
        hasHyperlinks = true;
        hyperlinkStarted = false;
      }
      else
      {
        hasHyperlinks = startHyperlink(image);
      }
     
      writer.write("<img");
      String imagePath = null;
      String imageMapName = null;
      List imageMapAreas = null;
 
      ScaleImageEnum scaleImage = image.getScaleImageValue();
     
      if (renderer != null)
      {
        if (renderer.getType() == JRRenderable.TYPE_IMAGE && rendererToImagePathMap.containsKey(renderer.getId()))
        {
          imagePath = (String)rendererToImagePathMap.get(renderer.getId());
        }
        else
        {
          if (image.isLazy())
          {
            imagePath = ((JRImageRenderer)renderer).getImageLocation();
          }
          else
          {
            JRPrintElementIndex imageIndex = getElementIndex(gridCell);
            imagesToProcess.add(imageIndex);
 
            String imageName = getImageName(imageIndex);
            imagePath = imagesURI + imageName;
 
            //backward compatibility with the IMAGE_MAP parameter
            if (imageNameToImageDataMap != null)
            {
              if (renderer.getType() == JRRenderable.TYPE_SVG)
              {
                renderer =
                  new JRWrappingSvgRenderer(
                    renderer,
                    new Dimension(image.getWidth(), image.getHeight()),
                    ModeEnum.OPAQUE == image.getModeValue() ? image.getBackcolor() : null
                    );
              }
              imageNameToImageDataMap.put(imageName, renderer.getImageData());
            }
            //END - backward compatibility with the IMAGE_MAP parameter
          }
 
          rendererToImagePathMap.put(renderer.getId(), imagePath);
        }
       
        if (imageMapRenderer)
        {
          Rectangle renderingArea = new Rectangle(image.getWidth(), image.getHeight());
         
          if (renderer.getType() == JRRenderable.TYPE_IMAGE)
          {
            imageMapName = (String) imageMaps.get(new Pair(renderer.getId(), renderingArea));
          }
 
          if (imageMapName == null)
          {
            imageMapName = "map_" + getElementIndex(gridCell).toString();
            imageMapAreas = ((JRImageMapRenderer) originalRenderer).getImageAreaHyperlinks(renderingArea);//FIXMECHART
           
            if (renderer.getType() == JRRenderable.TYPE_IMAGE)
            {
              imageMaps.put(new Pair(renderer.getId(), renderingArea), imageMapName);
            }
          }
        }
      }
      else     // ie:   if(isUsingImagesToAlign)
      {
        loadPxImage();
        imagePath = imagesURI + "px";
        scaleImage = ScaleImageEnum.FILL_FRAME;
      }
 
      writer.write(" src=\"");
      if (imagePath != null)
      {
        writer.write(imagePath);
      }
      writer.write("\"");
   
      int imageWidth = image.getWidth() - image.getLineBox().getLeftPadding().intValue() - image.getLineBox().getRightPadding().intValue();
      if (imageWidth < 0)
      {
        imageWidth = 0;
      }
   
      int imageHeight = image.getHeight() - image.getLineBox().getTopPadding().intValue() - image.getLineBox().getBottomPadding().intValue();
      if (imageHeight < 0)
      {
        imageHeight = 0;
      }
   
      switch (scaleImage)
      {
        case FILL_FRAME :
        {
          writer.write(" style=\"width: ");
          writer.write(toSizeUnit(imageWidth));
          writer.write("; height: ");
          writer.write(toSizeUnit(imageHeight));
          writer.write("\"");
   
          break;
        }
        case CLIP : //FIXMEIMAGE image clip could be achieved by cutting the image and preserving the image type
        case RETAIN_SHAPE :
        default :
        {
          double normalWidth = imageWidth;
          double normalHeight = imageHeight;
   
          if (!image.isLazy())
          {
            // Image load might fail.
            JRRenderable tmpRenderer =
              JRImageRenderer.getOnErrorRendererForDimension(renderer, image.getOnErrorTypeValue());
            Dimension2D dimension = tmpRenderer == null ? null : tmpRenderer.getDimension();
            // If renderer was replaced, ignore image dimension.
            if (tmpRenderer == renderer && dimension != null)
            {
              normalWidth = dimension.getWidth();
              normalHeight = dimension.getHeight();
            }
          }
   
          if (imageHeight > 0)
          {
            double ratio = normalWidth / normalHeight;
   
            if( ratio > (double)imageWidth / (double)imageHeight )
            {
              writer.write(" style=\"width: ");
              writer.write(toSizeUnit(imageWidth));
              writer.write("\"");
            }
            else
            {
              writer.write(" style=\"height: ");
              writer.write(toSizeUnit(imageHeight));
              writer.write("\"");
            }
          }
        }
      }
     
      if (imageMapName != null)
      {
        writer.write(" usemap=\"#" + imageMapName + "\"");
      }
     
      writer.write(" alt=\"\"");
     
      if (hasHyperlinks)
      {
        writer.write(" border=\"0\"");
      }
     
      if (image.getHyperlinkTooltip() != null)
      {
        writer.write(" title=\"");
        writer.write(JRStringUtil.xmlEncode(image.getHyperlinkTooltip()));
        writer.write("\"");
      }
     
      writer.write("/>");

      endHyperlink();
     
      if (imageMapAreas != null)
      {
        writer.write("\n");
        writeImageMap(imageMapName, image, imageMapAreas);
      }
    }
    writeCellEnd(gridCell);
  }


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


  protected void writeImageMap(String imageMapName, JRPrintImage image, List imageMapAreas) throws IOException
  {
    writer.write("<map name=\"" + imageMapName + "\">\n");

    for (ListIterator it = imageMapAreas.listIterator(imageMapAreas.size()); it.hasPrevious();)
    {
      JRPrintImageAreaHyperlink areaHyperlink = (JRPrintImageAreaHyperlink) it.previous();
      JRPrintImageArea area = areaHyperlink.getArea();

      writer.write("  <area shape=\"" + JRPrintImageArea.getHtmlShape(area.getShape()) + "\"");
      writeImageAreaCoordinates(area.getCoordinates());     
      writeImageAreaHyperlink(areaHyperlink.getHyperlink());
      writer.write("/>\n");
    }
   
    if (image.getHyperlinkTypeValue() != HyperlinkTypeEnum.NONE)
    {
      writer.write("  <area shape=\"default\"");
      writeImageAreaCoordinates(new int[]{0, 0, image.getWidth(), image.getHeight()});//for IE
      writeImageAreaHyperlink(image);
      writer.write("/>\n");
    }
   
    writer.write("</map>\n");
  }

 
  protected void writeImageAreaCoordinates(int[] coords) throws IOException
  {
    if (coords != null && coords.length > 0)
    {
      StringBuffer coordsEnum = new StringBuffer(coords.length * 4);
      coordsEnum.append(toZoom(coords[0]));
      for (int i = 1; i < coords.length; i++)
      {
        coordsEnum.append(',');
        coordsEnum.append(toZoom(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("\"");
    }
  }


  /**
   *
   */
  protected void loadPxImage() throws JRException
  {
    isPxImageLoaded = true;
    //backward compatibility with the IMAGE_MAP parameter
    if (imageNameToImageDataMap != null && !imageNameToImageDataMap.containsKey("px"))
    {
      JRRenderable pxRenderer =
        JRImageRenderer.getInstance("net/sf/jasperreports/engine/images/pixel.GIF");
      rendererToImagePathMap.put(pxRenderer.getId(), imagesURI + "px");
      imageNameToImageDataMap.put("px", pxRenderer.getImageData());
    }
    //END - backward compatibility with the IMAGE_MAP parameter
  }


  /**
   *
   */
  protected static interface StringProvider
  {

    /**
     *
     */
    public String getStringForCollapsedTD(Object value, int width, int height);

    /**
     *
     */
    public String getStringForEmptyTD(Object value);

    public String getReportTableStyle();
  }


  /**
   *
   */
  private boolean appendPadding(StringBuffer sb, Integer padding, String side)
  {
    boolean addedToStyle = false;
   
    if (padding.intValue() > 0)
    {
      sb.append("padding");
      if (side != null)
      {
        sb.append("-");
        sb.append(side);
      }
      sb.append(": ");
      sb.append(toSizeUnit(padding.intValue()));
      sb.append("; ");

      addedToStyle = true;
    }
   
    return addedToStyle;
  }


  /**
   *
   */
  private boolean appendPen(StringBuffer sb, JRPen pen, String side)
  {
    boolean addedToStyle = false;
   
    float borderWidth = pen.getLineWidth().floatValue();
    if (0f < borderWidth && borderWidth < 1f)
    {
      borderWidth = 1f;
    }

    String borderStyle = null;
    switch (pen.getLineStyleValue())
    {
      case DOUBLE :
      {
        borderStyle = "double";
        break;
      }
      case DOTTED :
      {
        borderStyle = "dotted";
        break;
      }
      case DASHED :
      {
        borderStyle = "dashed";
        break;
      }
      case SOLID :
      default :
      {
        borderStyle = "solid";
        break;
      }
    }

    if (borderWidth > 0f)
    {
      sb.append("border");
      if (side != null)
      {
        sb.append("-");
        sb.append(side);
      }
      sb.append("-style: ");
      sb.append(borderStyle);
      sb.append("; ");

      sb.append("border");
      if (side != null)
      {
        sb.append("-");
        sb.append(side);
      }
      sb.append("-width: ");
      sb.append(toSizeUnit((int)borderWidth));
      sb.append("; ");

      sb.append("border");
      if (side != null)
      {
        sb.append("-");
        sb.append(side);
      }
      sb.append("-color: #");
      sb.append(JRColorUtil.getColorHexa(pen.getLineColor()));
      sb.append("; ");

      addedToStyle = true;
    }

    return addedToStyle;
  }


  /**
   *
   */
  public static String getImageName(JRPrintElementIndex printElementIndex)
  {
    return IMAGE_NAME_PREFIX + printElementIndex.toString();
  }


  /**
   *
   */
  public static JRPrintElementIndex getPrintElementIndex(String imageName)
  {
    if (!imageName.startsWith(IMAGE_NAME_PREFIX))
    {
      throw new JRRuntimeException("Invalid image name: " + imageName);
    }

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


  protected void exportFrame(JRPrintFrame frame, JRExporterGridCell gridCell) throws IOException, JRException
  {
    writeCellStart(gridCell);

    StringBuffer styleBuffer = new StringBuffer();
    Color frameBackcolor = appendBackcolorStyle(gridCell, styleBuffer);
    appendBorderStyle(gridCell.getBox(), styleBuffer);
    //no padding style for frames, because padding is in the grid

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

    writer.write(">\n");

    if (frameBackcolor != null)
    {
      setBackcolor(frameBackcolor);
    }
    try
    {
      exportGrid(gridCell.getLayout(), false);
    }
    finally
    {
      if (frameBackcolor != null)
      {
        restoreBackcolor();
      }
    }

    writeCellEnd(gridCell);
  }


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

    backcolor = color;
  }


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


  protected void exportGenericElement(JRGenericPrintElement element,
      JRExporterGridCell gridCell, int rowHeight) throws IOException
  {
    GenericElementHtmlHandler handler = (GenericElementHtmlHandler)
        GenericElementHandlerEnviroment.getHandler(
            element.getGenericType(), HTML_EXPORTER_KEY);
   
    if (handler == null)
    {
      if (log.isDebugEnabled())
      {
        log.debug("No HTML generic element handler for "
            + element.getGenericType());
      }
     
      writeEmptyCell(gridCell, rowHeight);
    }
    else
    {
      writeCellStart(gridCell);

      StringBuffer styleBuffer = new StringBuffer();
      appendBackcolorStyle(gridCell, styleBuffer);
      appendBorderStyle(gridCell.getBox(), styleBuffer);
      if (styleBuffer.length() > 0)
      {
        writer.write(" style=\"");
        writer.write(styleBuffer.toString());
        writer.write("\"");
      }

      writer.write(">");
     
      String htmlFragment = handler.getHtmlFragment(exporterContext, element);
      if (htmlFragment != null)
      {
        writer.write(htmlFragment);
      }

      writeCellEnd(gridCell);
    }
  }

  public Map getExportParameters()
  {
    return parameters;
  }

  public String getExportPropertiesPrefix()
  {
    return HTML_EXPORTER_PROPERTIES_PREFIX;
  }
 
  protected String getExporterKey()
  {
    return HTML_EXPORTER_KEY;
  }

  public JasperPrint getExportedReport()
  {
    return jasperPrint;
  }

  public String toSizeUnit(int size)
  {
    return String.valueOf(toZoom(size)) + sizeUnit;
  }

  public int toZoom(int size)
  {
    return (int)(zoom * size);
  }


  protected JRStyledText getStyledText(JRPrintText textElement,
      boolean setBackcolor)
  {
    JRStyledText styledText = super.getStyledText(textElement, setBackcolor);
   
    if (styledText != null)
    {
      short[] lineBreakOffsets = textElement.getLineBreakOffsets();
      if (lineBreakOffsets != null && lineBreakOffsets.length > 0)
      {
        //insert new lines at the line break positions saved at fill time
        //cloning the text first
        styledText = styledText.cloneText();
        styledText.insert("\n", lineBreakOffsets);
      }
    }
   
    return styledText;
  }
 
}
TOP

Related Classes of net.sf.jasperreports.engine.export.JRHtmlExporter$ExporterContext

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.