Package net.sf.jasperreports.engine.export

Source Code of net.sf.jasperreports.engine.export.JRPdfExporter$Bookmark

/*
* 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:
* Adrian Jackson - iapetus@users.sourceforge.net
* David Taylor - exodussystems@users.sourceforge.net
* Lars Kristensen - llk@users.sourceforge.net
* Ling Li - lonecatz@users.sourceforge.net
* Martin Clough - mtclough@users.sourceforge.net
*/
package net.sf.jasperreports.engine.export;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.font.TextAttribute;
import java.awt.geom.AffineTransform;
import java.awt.geom.Dimension2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import net.sf.jasperreports.engine.JRAbstractExporter;
import net.sf.jasperreports.engine.JRAnchor;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExporterParameter;
import net.sf.jasperreports.engine.JRFont;
import net.sf.jasperreports.engine.JRGenericPrintElement;
import net.sf.jasperreports.engine.JRImageRenderer;
import net.sf.jasperreports.engine.JRLineBox;
import net.sf.jasperreports.engine.JRPen;
import net.sf.jasperreports.engine.JRPrintAnchor;
import net.sf.jasperreports.engine.JRPrintElement;
import net.sf.jasperreports.engine.JRPrintEllipse;
import net.sf.jasperreports.engine.JRPrintFrame;
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.JRPrintRectangle;
import net.sf.jasperreports.engine.JRPrintText;
import net.sf.jasperreports.engine.JRRenderable;
import net.sf.jasperreports.engine.JRReportFont;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JRStyle;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.base.JRBaseFont;
import net.sf.jasperreports.engine.export.legacy.BorderOffset;
import net.sf.jasperreports.engine.fonts.FontFace;
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.LineStyleEnum;
import net.sf.jasperreports.engine.type.ModeEnum;
import net.sf.jasperreports.engine.util.BreakIteratorSplitCharacter;
import net.sf.jasperreports.engine.util.JRFontUtil;
import net.sf.jasperreports.engine.util.JRLoader;
import net.sf.jasperreports.engine.util.JRProperties;
import net.sf.jasperreports.engine.util.JRStyledText;
import net.sf.jasperreports.engine.util.JRTextAttribute;

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

import com.lowagie.text.Chunk;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.FontFactory;
import com.lowagie.text.Image;
import com.lowagie.text.Phrase;
import com.lowagie.text.Rectangle;
import com.lowagie.text.SplitCharacter;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.ColumnText;
import com.lowagie.text.pdf.FontMapper;
import com.lowagie.text.pdf.PdfAction;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfDestination;
import com.lowagie.text.pdf.PdfName;
import com.lowagie.text.pdf.PdfOutline;
import com.lowagie.text.pdf.PdfTemplate;
import com.lowagie.text.pdf.PdfWriter;


/**
* Exports a JasperReports document to PDF format. It has binary output type and exports the document to
* a free-form layout.
*
* @author Teodor Danciu (teodord@users.sourceforge.net)
* @version $Id: JRPdfExporter.java 4288 2011-04-13 13:24:53Z teodord $
*/
public class JRPdfExporter extends JRAbstractExporter
{

  private static final Log log = LogFactory.getLog(JRPdfExporter.class);
 
  /**
   * @deprecated Replaced by {@link JRPdfExporterParameter#PROPERTY_FORCE_SVG_SHAPES}.
   */
  public static final String PDF_FORCE_SVG_SHAPES = JRPdfExporterParameter.PROPERTY_FORCE_SVG_SHAPES;

  public static final String PDF_EXPORTER_PROPERTIES_PREFIX = JRProperties.PROPERTY_PREFIX + "export.pdf.";

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

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

  protected static boolean fontsRegistered;

  protected class ExporterContext extends BaseExporterContext implements JRPdfExporterContext
  {
    public String getExportPropertiesPrefix()
    {
      return PDF_EXPORTER_PROPERTIES_PREFIX;
    }

    public PdfWriter getPdfWriter()
    {
      return pdfWriter;
    }
  }
 
  /**
   *
   */
  protected Document document;
  protected PdfContentByte pdfContentByte;
  protected PdfWriter pdfWriter;

  protected Document imageTesterDocument;
  protected PdfContentByte imageTesterPdfContentByte;
 
  protected JRPdfExporterTagHelper tagHelper = new JRPdfExporterTagHelper(this);

  protected JRExportProgressMonitor progressMonitor;

  protected int reportIndex;

  /**
   *
   */
  protected boolean forceSvgShapes;
  protected boolean isCreatingBatchModeBookmarks;
  protected boolean isCompressed;
  protected boolean isEncrypted;
  protected boolean is128BitKey;
  protected String userPassword;
  protected String ownerPassword;
  protected int permissions;
  protected Character pdfVersion;
  protected String pdfJavaScript;
  protected String printScaling;
 
  private boolean collapseMissingBookmarkLevels;

  /**
   *
   */
  protected Map loadedImagesMap;
  protected Image pxImage;

  private BookmarkStack bookmarkStack;

  /**
   * @deprecated
   */
  private Map fontMap;

  private SplitCharacter splitCharacter;
 
  protected JRPdfExporterContext exporterContext = new ExporterContext();
 
  /**
   *
   */
  protected Image getPxImage()
  {
    if (pxImage == null)
    {
      try
      {
        pxImage =
          Image.getInstance(
            JRLoader.loadBytesFromLocation("net/sf/jasperreports/engine/images/pixel.GIF", null)
            );
      }
      catch(Exception e)
      {
        throw new JRRuntimeException(e);
      }
    }

    return pxImage;
  }


  /**
   *
   */
  public void exportReport() throws JRException
  {
    registerFonts();

    progressMonitor = (JRExportProgressMonitor)parameters.get(JRExporterParameter.PROGRESS_MONITOR);

    /*   */
    setOffset();

    try
    {
      /*   */
      setExportContext();

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

      /*   */
      if (!isModeBatch)
      {
        setPageRange();
      }

      isCreatingBatchModeBookmarks =
        getBooleanParameter(
          JRPdfExporterParameter.IS_CREATING_BATCH_MODE_BOOKMARKS,
          JRPdfExporterParameter.PROPERTY_CREATE_BATCH_MODE_BOOKMARKS,
          false
          );

      forceSvgShapes = //FIXME certain properties need to be read from each individual document in batch mode; check all exporters and all props
        getBooleanParameter(
          JRPdfExporterParameter.FORCE_SVG_SHAPES,
          JRPdfExporterParameter.PROPERTY_FORCE_SVG_SHAPES,
          false
          );

      isCompressed =
        getBooleanParameter(
          JRPdfExporterParameter.IS_COMPRESSED,
          JRPdfExporterParameter.PROPERTY_COMPRESSED,
          false
          );

      isEncrypted =
        getBooleanParameter(
          JRPdfExporterParameter.IS_ENCRYPTED,
          JRPdfExporterParameter.PROPERTY_ENCRYPTED,
          false
          );

      is128BitKey =
        getBooleanParameter(
          JRPdfExporterParameter.IS_128_BIT_KEY,
          JRPdfExporterParameter.PROPERTY_128_BIT_KEY,
          false
          );

      userPassword =
        getStringParameter(
          JRPdfExporterParameter.USER_PASSWORD,
          JRPdfExporterParameter.PROPERTY_USER_PASSWORD
          );

      ownerPassword =
        getStringParameter(
          JRPdfExporterParameter.OWNER_PASSWORD,
          JRPdfExporterParameter.PROPERTY_OWNER_PASSWORD
          );

      Integer permissionsParameter = (Integer)parameters.get(JRPdfExporterParameter.PERMISSIONS);
      if (permissionsParameter != null)
      {
        permissions = permissionsParameter.intValue();
      }

      pdfVersion =
        getCharacterParameter(
            JRPdfExporterParameter.PDF_VERSION,
            JRPdfExporterParameter.PROPERTY_PDF_VERSION
            );

      fontMap = (Map) parameters.get(JRExporterParameter.FONT_MAP);

      setSplitCharacter();
      setHyperlinkProducerFactory();

      pdfJavaScript =
        getStringParameter(
          JRPdfExporterParameter.PDF_JAVASCRIPT,
          JRPdfExporterParameter.PROPERTY_PDF_JAVASCRIPT
          );

      printScaling =
        getStringParameter(
          JRPdfExporterParameter.PRINT_SCALING,
          JRPdfExporterParameter.PROPERTY_PRINT_SCALING
          );

      tagHelper.setTagged(
        getBooleanParameter(
          JRPdfExporterParameter.IS_TAGGED,
          JRPdfExporterParameter.PROPERTY_TAGGED,
          false
          )
        );
     
      // no export param for this
      collapseMissingBookmarkLevels = JRProperties.getBooleanProperty(jasperPrint,
          JRPdfExporterParameter.PROPERTY_COLLAPSE_MISSING_BOOKMARK_LEVELS, false);

      OutputStream os = (OutputStream)parameters.get(JRExporterParameter.OUTPUT_STREAM);
      if (os != null)
      {
        exportReportToStream(os);
      }
      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);
          exportReportToStream(os);
          os.flush();
        }
        catch (IOException e)
        {
          throw new JRException("Error trying to export to file : " + destFile, e);
        }
        finally
        {
          if (os != null)
          {
            try
            {
              os.close();
            }
            catch(IOException e)
            {
            }
          }
        }
      }
    }
    finally
    {
      resetExportContext();
    }
  }


  protected void setSplitCharacter()
  {
    boolean useFillSplitCharacter;
    Boolean useFillSplitCharacterParam = (Boolean) parameters.get(JRPdfExporterParameter.FORCE_LINEBREAK_POLICY);
    if (useFillSplitCharacterParam == null)
    {
      useFillSplitCharacter =
        JRProperties.getBooleanProperty(
          jasperPrint.getPropertiesMap(),
          JRPdfExporterParameter.PROPERTY_FORCE_LINEBREAK_POLICY,
          false
          );
    }
    else
    {
      useFillSplitCharacter = useFillSplitCharacterParam.booleanValue();
    }

    if (useFillSplitCharacter)
    {
      splitCharacter = new BreakIteratorSplitCharacter();
    }
  }


  /**
   *
   */
  protected void exportReportToStream(OutputStream os) throws JRException
  {
    //ByteArrayOutputStream baos = new ByteArrayOutputStream();

    document =
      new Document(
        new Rectangle(
          jasperPrint.getPageWidth(),
          jasperPrint.getPageHeight()
        )
      );

    imageTesterDocument =
      new Document(
        new Rectangle(
          10, //jasperPrint.getPageWidth(),
          10 //jasperPrint.getPageHeight()
        )
      );

    boolean closeDocuments = true;
    try
    {
      pdfWriter = PdfWriter.getInstance(document, os);
      pdfWriter.setCloseStream(false);

      tagHelper.setPdfWriter(pdfWriter);
     
      if (pdfVersion != null)
      {
        pdfWriter.setPdfVersion(pdfVersion.charValue());
      }
      if (isCompressed)
      {
        pdfWriter.setFullCompression();
      }
      if (isEncrypted)
      {
        pdfWriter.setEncryption(
          is128BitKey,
          userPassword,
          ownerPassword,
          permissions
          );
      }
     
      pdfWriter.setRgbTransparencyBlending(true);

      if (printScaling != null)
      {
        if (JRPdfExporterParameter.PRINT_SCALING_DEFAULT.equals(printScaling))
        {
          pdfWriter.addViewerPreference(PdfName.PRINTSCALING, PdfName.APPDEFAULT);
        }
        else if (JRPdfExporterParameter.PRINT_SCALING_NONE.equals(printScaling))
        {
          pdfWriter.addViewerPreference(PdfName.PRINTSCALING, PdfName.NONE);
        }
      }

      // Add meta-data parameters to generated PDF document
      // mtclough@users.sourceforge.net 2005-12-05
      String title = (String)parameters.get(JRPdfExporterParameter.METADATA_TITLE);
      if( title != null )
      {
        document.addTitle(title);
      }
      String author = (String)parameters.get(JRPdfExporterParameter.METADATA_AUTHOR);
      if( author != null )
      {
        document.addAuthor(author);
      }
      String subject = (String)parameters.get(JRPdfExporterParameter.METADATA_SUBJECT);
      if( subject != null )
      {
        document.addSubject(subject);
      }
      String keywords = (String)parameters.get(JRPdfExporterParameter.METADATA_KEYWORDS);
      if( keywords != null )
      {
        document.addKeywords(keywords);
      }
      String creator = (String)parameters.get(JRPdfExporterParameter.METADATA_CREATOR);
      if( creator != null )
      {
        document.addCreator(creator);
      }
      else
      {
        document.addCreator("JasperReports (" + jasperPrint.getName() + ")");
      }

      document.open();
     
      if(pdfJavaScript != null)
      {
        pdfWriter.addJavaScript(pdfJavaScript);
      }

      pdfContentByte = pdfWriter.getDirectContent();

      tagHelper.init(pdfContentByte);
     
      initBookmarks();

      PdfWriter imageTesterPdfWriter =
        PdfWriter.getInstance(
          imageTesterDocument,
          new NullOutputStream() // discard the output
          );
      imageTesterDocument.open();
      imageTesterDocument.newPage();
      imageTesterPdfContentByte = imageTesterPdfWriter.getDirectContent();
      imageTesterPdfContentByte.setLiteral("\n");

      for(reportIndex = 0; reportIndex < jasperPrintList.size(); reportIndex++)
      {
        setJasperPrint((JasperPrint)jasperPrintList.get(reportIndex));
        loadedImagesMap = new HashMap();
       
        Rectangle pageSize;
        switch (jasperPrint.getOrientationValue())
        {
        case LANDSCAPE:
          // using rotate to indicate landscape page
          pageSize = new Rectangle(jasperPrint.getPageHeight(),
              jasperPrint.getPageWidth()).rotate();
          break;
        default:
          pageSize = new Rectangle(jasperPrint.getPageWidth(),
              jasperPrint.getPageHeight());
          break;
        }
        document.setPageSize(pageSize);
       
        BorderOffset.setLegacy(
          JRProperties.getBooleanProperty(jasperPrint, BorderOffset.PROPERTY_LEGACY_BORDER_OFFSET, false)
          );

        List pages = jasperPrint.getPages();
        if (pages != null && pages.size() > 0)
        {
          if (isModeBatch)
          {
            document.newPage();

            if( isCreatingBatchModeBookmarks )
            {
              //add a new level to our outline for this report
              addBookmark(0, jasperPrint.getName(), 0, 0);
            }

            startPageIndex = 0;
            endPageIndex = pages.size() - 1;
          }

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

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

            document.newPage();
           
            pdfContentByte = pdfWriter.getDirectContent();

            pdfContentByte.setLineCap(2);//PdfContentByte.LINE_CAP_PROJECTING_SQUARE since iText 1.02b

            writePageAnchor(pageIndex);

            /*   */
            exportPage(page);
          }
        }
        else
        {
          document.newPage();
          pdfContentByte = pdfWriter.getDirectContent();
          pdfContentByte.setLiteral("\n");
        }
      }

      closeDocuments = false;
      document.close();
      imageTesterDocument.close();
    }
    catch(DocumentException e)
    {
      throw new JRException("PDF Document error : " + jasperPrint.getName(), e);
    }
    catch(IOException e)
    {
      throw new JRException("Error generating PDF report : " + jasperPrint.getName(), e);
    }
    finally
    {
      if (closeDocuments) //only on exception
      {
        try
        {
          document.close();
        }
        catch (Exception e)
        {
          // ignore, let the original exception propagate
        }

        try
        {
          imageTesterDocument.close();
        }
        catch (Exception e)
        {
          // ignore, let the original exception propagate
        }
      }
    }

    //return os.toByteArray();
  }


  protected void writePageAnchor(int pageIndex) throws DocumentException {
    Map pdfFontAttrs = getDefaultPdfFontAttributes();
    Chunk chunk;
    if (pdfFontAttrs == null)
    {
      chunk = new Chunk(" ");
    }
    else
    {
      // no underline or strikethrough
      Font pdfFont = getFont(pdfFontAttrs, getLocale(), false);
      chunk = new Chunk(" ", pdfFont);
    }
   
    chunk.setLocalDestination(JR_PAGE_ANCHOR_PREFIX + reportIndex + "_" + (pageIndex + 1));

    tagHelper.startPageAnchor();
   
    ColumnText colText = new ColumnText(pdfContentByte);
    colText.setSimpleColumn(
      new Phrase(chunk),
      0,
      jasperPrint.getPageHeight(),
      1,
      1,
      0,
      Element.ALIGN_LEFT
      );

    colText.go();

    tagHelper.endPageAnchor();
  }

  protected Map getDefaultPdfFontAttributes() {
    Map attrs;
    JRStyle style = jasperPrint.getDefaultStyle();
    if (style != null)
    {
      attrs = new HashMap();
      //FIXME font name for extensions
      attrs.put(JRTextAttribute.PDF_FONT_NAME, style.getPdfFontName());
      attrs.put(JRTextAttribute.PDF_ENCODING, style.getPdfEncoding());
      attrs.put(JRTextAttribute.IS_PDF_EMBEDDED, style.isPdfEmbedded());
    }
    else
    {
      JRReportFont font = jasperPrint.getDefaultFont();
      if (font != null)
      {
        attrs = new HashMap();
        attrs.put(JRTextAttribute.PDF_FONT_NAME, font.getPdfFontName());
        attrs.put(JRTextAttribute.PDF_ENCODING, font.getPdfEncoding());
        attrs.put(JRTextAttribute.IS_PDF_EMBEDDED, font.isPdfEmbedded() ? Boolean.TRUE : Boolean.FALSE);
      }
      else
      {
        attrs = null;
      }
    }
    return attrs;
  }

  /**
   *
   */
  protected void exportPage(JRPrintPage page) throws JRException, DocumentException, IOException
  {
    tagHelper.startPage();
   
    Collection elements = page.getElements();
    exportElements(elements);
   
    tagHelper.endPage();

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

  protected void exportElements(Collection elements) throws DocumentException, IOException, JRException
  {
    if (elements != null && elements.size() > 0)
    {
      for(Iterator it = elements.iterator(); it.hasNext();)
      {
        JRPrintElement element = (JRPrintElement)it.next();

        if (filter == null || filter.isToExport(element))
        {
          tagHelper.startElement(element);

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

          tagHelper.endElement(element);
        }
      }
    }
  }


  /**
   *
   */
  protected void exportLine(JRPrintLine line)
  {
    float lineWidth = line.getLinePen().getLineWidth().floatValue();
    if (lineWidth > 0f)
    {
      preparePen(pdfContentByte, line.getLinePen(), PdfContentByte.LINE_CAP_BUTT);

      if (line.getWidth() == 1)
      {
        if (line.getHeight() != 1)
        {
          //Vertical line
          if (line.getLinePen().getLineStyleValue() == LineStyleEnum.DOUBLE)
          {
            pdfContentByte.moveTo(
              line.getX() + getOffsetX() + 0.5f - lineWidth / 3,
              jasperPrint.getPageHeight() - line.getY() - getOffsetY()
              );
            pdfContentByte.lineTo(
              line.getX() + getOffsetX() + 0.5f - lineWidth / 3,
              jasperPrint.getPageHeight() - line.getY() - getOffsetY() - line.getHeight()
              );

            pdfContentByte.stroke();
           
            pdfContentByte.moveTo(
              line.getX() + getOffsetX() + 0.5f + lineWidth / 3,
              jasperPrint.getPageHeight() - line.getY() - getOffsetY()
              );
            pdfContentByte.lineTo(
              line.getX() + getOffsetX() + 0.5f + lineWidth / 3,
              jasperPrint.getPageHeight() - line.getY() - getOffsetY() - line.getHeight()
              );
          }
          else
          {
            pdfContentByte.moveTo(
              line.getX() + getOffsetX() + 0.5f,
              jasperPrint.getPageHeight() - line.getY() - getOffsetY()
              );
            pdfContentByte.lineTo(
              line.getX() + getOffsetX() + 0.5f,
              jasperPrint.getPageHeight() - line.getY() - getOffsetY() - line.getHeight()
              );
          }
        }
      }
      else
      {
        if (line.getHeight() == 1)
        {
          //Horizontal line
          if (line.getLinePen().getLineStyleValue() == LineStyleEnum.DOUBLE)
          {
            pdfContentByte.moveTo(
              line.getX() + getOffsetX(),
              jasperPrint.getPageHeight() - line.getY() - getOffsetY() - 0.5f + lineWidth / 3
              );
            pdfContentByte.lineTo(
              line.getX() + getOffsetX() + line.getWidth(),
              jasperPrint.getPageHeight() - line.getY() - getOffsetY() - 0.5f + lineWidth / 3
              );

            pdfContentByte.stroke();
           
            pdfContentByte.moveTo(
              line.getX() + getOffsetX(),
              jasperPrint.getPageHeight() - line.getY() - getOffsetY() - 0.5f - lineWidth / 3
              );
            pdfContentByte.lineTo(
              line.getX() + getOffsetX() + line.getWidth(),
              jasperPrint.getPageHeight() - line.getY() - getOffsetY() - 0.5f - lineWidth / 3
              );
          }
          else
          {
            pdfContentByte.moveTo(
              line.getX() + getOffsetX(),
              jasperPrint.getPageHeight() - line.getY() - getOffsetY() - 0.5f
              );
            pdfContentByte.lineTo(
              line.getX() + getOffsetX() + line.getWidth(),
              jasperPrint.getPageHeight() - line.getY() - getOffsetY() - 0.5f
              );
          }
        }
        else
        {
          //Oblique line
          if (line.getDirectionValue() == LineDirectionEnum.TOP_DOWN)
          {
            if (line.getLinePen().getLineStyleValue() == LineStyleEnum.DOUBLE)
            {
              double xtrans = lineWidth / (3 * Math.sqrt(1 + Math.pow(line.getWidth(), 2) / Math.pow(line.getHeight(), 2)));
              double ytrans = lineWidth / (3 * Math.sqrt(1 + Math.pow(line.getHeight(), 2) / Math.pow(line.getWidth(), 2)));
             
              pdfContentByte.moveTo(
                line.getX() + getOffsetX() + (float)xtrans,
                jasperPrint.getPageHeight() - line.getY() - getOffsetY() + (float)ytrans
                );
              pdfContentByte.lineTo(
                line.getX() + getOffsetX() + line.getWidth() + (float)xtrans,
                jasperPrint.getPageHeight() - line.getY() - getOffsetY() - line.getHeight() + (float)ytrans
                );

              pdfContentByte.stroke();
             
              pdfContentByte.moveTo(
                line.getX() + getOffsetX() - (float)xtrans,
                jasperPrint.getPageHeight() - line.getY() - getOffsetY() - (float)ytrans
                );
              pdfContentByte.lineTo(
                line.getX() + getOffsetX() + line.getWidth() - (float)xtrans,
                jasperPrint.getPageHeight() - line.getY() - getOffsetY() - line.getHeight() - (float)ytrans
                );
            }
            else
            {
              pdfContentByte.moveTo(
                line.getX() + getOffsetX(),
                jasperPrint.getPageHeight() - line.getY() - getOffsetY()
                );
              pdfContentByte.lineTo(
                line.getX() + getOffsetX() + line.getWidth(),
                jasperPrint.getPageHeight() - line.getY() - getOffsetY() - line.getHeight()
                );
            }
          }
          else
          {
            if (line.getLinePen().getLineStyleValue() == LineStyleEnum.DOUBLE)
            {
              double xtrans = lineWidth / (3 * Math.sqrt(1 + Math.pow(line.getWidth(), 2) / Math.pow(line.getHeight(), 2)));
              double ytrans = lineWidth / (3 * Math.sqrt(1 + Math.pow(line.getHeight(), 2) / Math.pow(line.getWidth(), 2)));
             
              pdfContentByte.moveTo(
                line.getX() + getOffsetX() + (float)xtrans,
                jasperPrint.getPageHeight() - line.getY() - getOffsetY() - line.getHeight() - (float)ytrans
                );
              pdfContentByte.lineTo(
                line.getX() + getOffsetX() + line.getWidth() + (float)xtrans,
                jasperPrint.getPageHeight() - line.getY() - getOffsetY() - (float)ytrans
                );

              pdfContentByte.stroke();

              pdfContentByte.moveTo(
                line.getX() + getOffsetX() - (float)xtrans,
                jasperPrint.getPageHeight() - line.getY() - getOffsetY() - line.getHeight() + (float)ytrans
                );
              pdfContentByte.lineTo(
                line.getX() + getOffsetX() + line.getWidth() - (float)xtrans,
                jasperPrint.getPageHeight() - line.getY() - getOffsetY() + (float)ytrans
                );
            }
            else
            {
              pdfContentByte.moveTo(
                line.getX() + getOffsetX(),
                jasperPrint.getPageHeight() - line.getY() - getOffsetY() - line.getHeight()
                );
              pdfContentByte.lineTo(
                line.getX() + getOffsetX() + line.getWidth(),
                jasperPrint.getPageHeight() - line.getY() - getOffsetY()
                );
            }
          }
        }
      }

      pdfContentByte.stroke();

      pdfContentByte.setLineDash(0f);
      pdfContentByte.setLineCap(PdfContentByte.LINE_CAP_PROJECTING_SQUARE);
    }
  }


  /**
   *
   */
  protected void exportRectangle(JRPrintRectangle rectangle)
  {
    pdfContentByte.setRGBColorFill(
      rectangle.getBackcolor().getRed(),
      rectangle.getBackcolor().getGreen(),
      rectangle.getBackcolor().getBlue()
      );

    preparePen(pdfContentByte, rectangle.getLinePen(), PdfContentByte.LINE_CAP_PROJECTING_SQUARE);

    float lineWidth = rectangle.getLinePen().getLineWidth().floatValue();
    float lineOffset = BorderOffset.getOffset(rectangle.getLinePen());
   
    if (rectangle.getModeValue() == ModeEnum.OPAQUE)
    {
      pdfContentByte.roundRectangle(
        rectangle.getX() + getOffsetX(),
        jasperPrint.getPageHeight() - rectangle.getY() - getOffsetY() - rectangle.getHeight(),
        rectangle.getWidth(),
        rectangle.getHeight(),
        rectangle.getRadius()
        );

      pdfContentByte.fill();
    }

    if (lineWidth > 0f)
    {
      if (rectangle.getLinePen().getLineStyleValue() == LineStyleEnum.DOUBLE)
      {
        pdfContentByte.roundRectangle(
          rectangle.getX() + getOffsetX() - lineWidth / 3,
          jasperPrint.getPageHeight() - rectangle.getY() - getOffsetY() - rectangle.getHeight() - lineWidth / 3,
          rectangle.getWidth() + 2 * lineWidth / 3,
          rectangle.getHeight() + 2 * lineWidth / 3,
          rectangle.getRadius()
          );

        pdfContentByte.stroke();
       
        pdfContentByte.roundRectangle(
          rectangle.getX() + getOffsetX() + lineWidth / 3,
          jasperPrint.getPageHeight() - rectangle.getY() - getOffsetY() - rectangle.getHeight() + lineWidth / 3,
          rectangle.getWidth() - 2 * lineWidth / 3,
          rectangle.getHeight() - 2 * lineWidth / 3,
          rectangle.getRadius()
          );
       
        pdfContentByte.stroke();
      }
      else
      {
        pdfContentByte.roundRectangle(
          rectangle.getX() + getOffsetX() + lineOffset,
          jasperPrint.getPageHeight() - rectangle.getY() - getOffsetY() - rectangle.getHeight() + lineOffset,
          rectangle.getWidth() - 2 * lineOffset,
          rectangle.getHeight() - 2 * lineOffset,
          rectangle.getRadius()
          );

        pdfContentByte.stroke();
      }
    }

    pdfContentByte.setLineDash(0f);
  }


  /**
   *
   */
  protected void exportEllipse(JRPrintEllipse ellipse)
  {
    pdfContentByte.setRGBColorFill(
      ellipse.getBackcolor().getRed(),
      ellipse.getBackcolor().getGreen(),
      ellipse.getBackcolor().getBlue()
      );

    preparePen(pdfContentByte, ellipse.getLinePen(), PdfContentByte.LINE_CAP_PROJECTING_SQUARE);

    float lineWidth = ellipse.getLinePen().getLineWidth().floatValue();
    float lineOffset = BorderOffset.getOffset(ellipse.getLinePen());
   
    if (ellipse.getModeValue() == ModeEnum.OPAQUE)
    {
      pdfContentByte.ellipse(
        ellipse.getX() + getOffsetX(),
        jasperPrint.getPageHeight() - ellipse.getY() - getOffsetY() - ellipse.getHeight(),
        ellipse.getX() + getOffsetX() + ellipse.getWidth(),
        jasperPrint.getPageHeight() - ellipse.getY() - getOffsetY()
        );

      pdfContentByte.fill();
    }

    if (lineWidth > 0f)
    {
      if (ellipse.getLinePen().getLineStyleValue() == LineStyleEnum.DOUBLE)
      {
        pdfContentByte.ellipse(
          ellipse.getX() + getOffsetX() - lineWidth / 3,
          jasperPrint.getPageHeight() - ellipse.getY() - getOffsetY() - ellipse.getHeight() - lineWidth / 3,
          ellipse.getX() + getOffsetX() + ellipse.getWidth() + lineWidth / 3,
          jasperPrint.getPageHeight() - ellipse.getY() - getOffsetY() + lineWidth / 3
          );

        pdfContentByte.stroke();

        pdfContentByte.ellipse(
          ellipse.getX() + getOffsetX() + lineWidth / 3,
          jasperPrint.getPageHeight() - ellipse.getY() - getOffsetY() - ellipse.getHeight() + lineWidth / 3,
          ellipse.getX() + getOffsetX() + ellipse.getWidth() - lineWidth / 3,
          jasperPrint.getPageHeight() - ellipse.getY() - getOffsetY() - lineWidth / 3
          );

        pdfContentByte.stroke();
      }
      else
      {
        pdfContentByte.ellipse(
          ellipse.getX() + getOffsetX() + lineOffset,
          jasperPrint.getPageHeight() - ellipse.getY() - getOffsetY() - ellipse.getHeight() + lineOffset,
          ellipse.getX() + getOffsetX() + ellipse.getWidth() - lineOffset,
          jasperPrint.getPageHeight() - ellipse.getY() - getOffsetY() - lineOffset
          );

        pdfContentByte.stroke();
      }
    }

    pdfContentByte.setLineDash(0f);
  }


  /**
   *
   */
  protected void exportImage(JRPrintImage printImage) throws DocumentException, IOException,  JRException
  {
    if (printImage.getModeValue() == ModeEnum.OPAQUE)
    {
      pdfContentByte.setRGBColorFill(
        printImage.getBackcolor().getRed(),
        printImage.getBackcolor().getGreen(),
        printImage.getBackcolor().getBlue()
        );
      pdfContentByte.rectangle(
        printImage.getX() + getOffsetX(),
        jasperPrint.getPageHeight() - printImage.getY() - getOffsetY(),
        printImage.getWidth(),
        - printImage.getHeight()
        );
      pdfContentByte.fill();
    }

    int topPadding = printImage.getLineBox().getTopPadding().intValue();
    int leftPadding = printImage.getLineBox().getLeftPadding().intValue();
    int bottomPadding = printImage.getLineBox().getBottomPadding().intValue();
    int rightPadding = printImage.getLineBox().getRightPadding().intValue();

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

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

    JRRenderable renderer = printImage.getRenderer();

    if (
      renderer != null &&
      availableImageWidth > 0 &&
      availableImageHeight > 0
      )
    {
      if (renderer.getType() == JRRenderable.TYPE_IMAGE)
      {
        // Image renderers are all asked for their image data at some point.
        // Better to test and replace the renderer now, in case of lazy load error.
        renderer = JRImageRenderer.getOnErrorRendererForImageData(renderer, printImage.getOnErrorTypeValue());
      }
    }
    else
    {
      renderer = null;
    }

    if (renderer != null)
    {
      int xoffset = 0;
      int yoffset = 0;

      Chunk chunk = null;

      float scaledWidth = availableImageWidth;
      float scaledHeight = availableImageHeight;

      if (renderer.getType() == JRRenderable.TYPE_IMAGE)
      {
        com.lowagie.text.Image image = null;

        float xalignFactor = getXAlignFactor(printImage);
        float yalignFactor = getYAlignFactor(printImage);

        switch(printImage.getScaleImageValue())
        {
          case CLIP :
          {
            // Image load might fail, from given image data.
            // Better to test and replace the renderer now, in case of lazy load error.
            renderer =
              JRImageRenderer.getOnErrorRendererForDimension(
                renderer,
                printImage.getOnErrorTypeValue()
                );
            if (renderer == null)
            {
              break;
            }
           
            int normalWidth = availableImageWidth;
            int normalHeight = availableImageHeight;

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

            xoffset = (int)(xalignFactor * (availableImageWidth - normalWidth));
            yoffset = (int)(yalignFactor * (availableImageHeight - normalHeight));

            int minWidth = Math.min(normalWidth, availableImageWidth);
            int minHeight = Math.min(normalHeight, availableImageHeight);

            BufferedImage bi =
              new BufferedImage(minWidth, minHeight, BufferedImage.TYPE_INT_ARGB);

            Graphics2D g = bi.createGraphics();
            if (printImage.getModeValue() == ModeEnum.OPAQUE)
            {
              g.setColor(printImage.getBackcolor());
              g.fillRect(0, 0, minWidth, minHeight);
            }
            renderer.render(
              g,
              new java.awt.Rectangle(
                (xoffset > 0 ? 0 : xoffset),
                (yoffset > 0 ? 0 : yoffset),
                normalWidth,
                normalHeight
                )
              );
            g.dispose();

            xoffset = (xoffset < 0 ? 0 : xoffset);
            yoffset = (yoffset < 0 ? 0 : yoffset);

            //awtImage = bi.getSubimage(0, 0, minWidth, minHeight);

            //image = com.lowagie.text.Image.getInstance(awtImage, printImage.getBackcolor());
            image = com.lowagie.text.Image.getInstance(bi, null);

            break;
          }
          case FILL_FRAME :
          {
            if (printImage.isUsingCache() && loadedImagesMap.containsKey(renderer))
            {
              image = (com.lowagie.text.Image)loadedImagesMap.get(renderer);
            }
            else
            {
              try
              {
                image = com.lowagie.text.Image.getInstance(renderer.getImageData());
                imageTesterPdfContentByte.addImage(image, 10, 0, 0, 10, 0, 0);
              }
              catch(Exception e)
              {
                JRImageRenderer tmpRenderer =
                  JRImageRenderer.getOnErrorRendererForImage(
                    JRImageRenderer.getInstance(renderer.getImageData()),
                    printImage.getOnErrorTypeValue()
                    );
                if (tmpRenderer == null)
                {
                  break;
                }
                java.awt.Image awtImage = tmpRenderer.getImage();
                image = com.lowagie.text.Image.getInstance(awtImage, null);
              }

              if (printImage.isUsingCache())
              {
                loadedImagesMap.put(renderer, image);
              }
            }

            image.scaleAbsolute(availableImageWidth, availableImageHeight);
            break;
          }
          case RETAIN_SHAPE :
          default :
          {
            if (printImage.isUsingCache() && loadedImagesMap.containsKey(renderer))
            {
              image = (com.lowagie.text.Image)loadedImagesMap.get(renderer);
            }
            else
            {
              try
              {
                image = com.lowagie.text.Image.getInstance(renderer.getImageData());
                imageTesterPdfContentByte.addImage(image, 10, 0, 0, 10, 0, 0);
              }
              catch(Exception e)
              {
                JRImageRenderer tmpRenderer =
                  JRImageRenderer.getOnErrorRendererForImage(
                    JRImageRenderer.getInstance(renderer.getImageData()),
                    printImage.getOnErrorTypeValue()
                    );
                if (tmpRenderer == null)
                {
                  break;
                }
                java.awt.Image awtImage = tmpRenderer.getImage();
                image = com.lowagie.text.Image.getInstance(awtImage, null);
              }

              if (printImage.isUsingCache())
              {
                loadedImagesMap.put(renderer, image);
              }
            }

            image.scaleToFit(availableImageWidth, availableImageHeight);

            xoffset = (int)(xalignFactor * (availableImageWidth - image.getPlainWidth()));
            yoffset = (int)(yalignFactor * (availableImageHeight - image.getPlainHeight()));

            xoffset = (xoffset < 0 ? 0 : xoffset);
            yoffset = (yoffset < 0 ? 0 : yoffset);

            break;
          }
        }

        if (image != null)
        {
          chunk = new Chunk(image, 0, 0);

          scaledWidth = image.getScaledWidth();
          scaledHeight = image.getScaledHeight();
        }
      }
      else
      {
        double normalWidth = availableImageWidth;
        double normalHeight = availableImageHeight;

        double displayWidth = availableImageWidth;
        double displayHeight = availableImageHeight;

        double ratioX = 1f;
        double ratioY = 1f;
       
        Rectangle2D clip = null;

        Dimension2D dimension = renderer.getDimension();
        if (dimension != null)
        {
          normalWidth = dimension.getWidth();
          normalHeight = dimension.getHeight();
          displayWidth = normalWidth;
          displayHeight = normalHeight;
         
          float xalignFactor = getXAlignFactor(printImage);
          float yalignFactor = getYAlignFactor(printImage);

          switch (printImage.getScaleImageValue())
          {
            case CLIP:
            {
              xoffset = (int) (xalignFactor * (availableImageWidth - normalWidth));
              yoffset = (int) (yalignFactor * (availableImageHeight - normalHeight));
              clip =
                new Rectangle2D.Double(
                  - xoffset,
                  - yoffset,
                  availableImageWidth,
                  availableImageHeight
                  );
              break;
            }
            case FILL_FRAME:
            {
              ratioX = availableImageWidth / normalWidth;
              ratioY = availableImageHeight / normalHeight;
              normalWidth *= ratioX;
              normalHeight *= ratioY;
              xoffset = 0;
              yoffset = 0;
              break;
            }
            case RETAIN_SHAPE:
            default:
            {
              ratioX = availableImageWidth / normalWidth;
              ratioY = availableImageHeight / normalHeight;
              ratioX = ratioX < ratioY ? ratioX : ratioY;
              ratioY = ratioX;
              normalWidth *= ratioX;
              normalHeight *= ratioY;
              xoffset = (int) (xalignFactor * (availableImageWidth - normalWidth));
              yoffset = (int) (yalignFactor * (availableImageHeight - normalHeight));
              break;
            }
          }
        }

        PdfTemplate template = pdfContentByte.createTemplate((float)displayWidth, (float)displayHeight);

        Graphics2D g = forceSvgShapes
          ? template.createGraphicsShapes((float)displayWidth, (float)displayHeight)
          : template.createGraphics(availableImageWidth, availableImageHeight, new LocalFontMapper());

        if (clip != null)
        {
          g.setClip(clip);
        }
       
        if (printImage.getModeValue() == ModeEnum.OPAQUE)
        {
          g.setColor(printImage.getBackcolor());
          g.fillRect(0, 0, (int)displayWidth, (int)displayHeight);
        }

        Rectangle2D rectangle = new Rectangle2D.Double(0, 0, displayWidth, displayHeight);

        renderer.render(g, rectangle);
        g.dispose();

        pdfContentByte.saveState();
        pdfContentByte.addTemplate(
          template,
          (float)ratioX, 0f, 0f, (float)ratioY,
          printImage.getX() + getOffsetX() + xoffset,
          jasperPrint.getPageHeight()
            - printImage.getY() - getOffsetY()
            - (int)normalHeight
            - yoffset
          );
        pdfContentByte.restoreState();

        Image image = getPxImage();
        image.scaleAbsolute(availableImageWidth, availableImageHeight);
        chunk = new Chunk(image, 0, 0);
      }

      /*
      image.setAbsolutePosition(
        printImage.getX() + offsetX + borderOffset,
        jasperPrint.getPageHeight() - printImage.getY() - offsetY - image.scaledHeight() - borderOffset
        );

      pdfContentByte.addImage(image);
      */


      if (chunk != null)
      {
        setAnchor(chunk, printImage, printImage);
        setHyperlinkInfo(chunk, printImage);

        tagHelper.startImage(printImage);
       
        ColumnText colText = new ColumnText(pdfContentByte);
        int upperY = jasperPrint.getPageHeight() - printImage.getY() - topPadding - getOffsetY() - yoffset;
        int lowerX = printImage.getX() + leftPadding + getOffsetX() + xoffset;
        colText.setSimpleColumn(
          new Phrase(chunk),
          lowerX,
          upperY - scaledHeight,
          lowerX + scaledWidth,
          upperY,
          scaledHeight,
          Element.ALIGN_LEFT
          );

        colText.go();

        tagHelper.endImage();
      }
    }


    if (
      printImage.getLineBox().getTopPen().getLineWidth().floatValue() <= 0f &&
      printImage.getLineBox().getLeftPen().getLineWidth().floatValue() <= 0f &&
      printImage.getLineBox().getBottomPen().getLineWidth().floatValue() <= 0f &&
      printImage.getLineBox().getRightPen().getLineWidth().floatValue() <= 0f
      )
    {
      if (printImage.getLinePen().getLineWidth().floatValue() > 0f)
      {
        exportPen(printImage.getLinePen(), printImage);
      }
    }
    else
    {
      /*   */
      exportBox(
        printImage.getLineBox(),
        printImage
        );
    }
  }


  private float getXAlignFactor(JRPrintImage printImage)
  {
    float xalignFactor = 0f;
    switch (printImage.getHorizontalAlignmentValue())
    {
      case RIGHT :
      {
        xalignFactor = 1f;
        break;
      }
      case CENTER :
      {
        xalignFactor = 0.5f;
        break;
      }
      case LEFT :
      default :
      {
        xalignFactor = 0f;
        break;
      }
    }
    return xalignFactor;
  }


  private float getYAlignFactor(JRPrintImage printImage)
  {
    float yalignFactor = 0f;
    switch (printImage.getVerticalAlignmentValue())
    {
      case BOTTOM :
      {
        yalignFactor = 1f;
        break;
      }
      case MIDDLE :
      {
        yalignFactor = 0.5f;
        break;
      }
      case TOP :
      default :
      {
        yalignFactor = 0f;
        break;
      }
    }
    return yalignFactor;
  }


  /**
   *
   */
  protected void setHyperlinkInfo(Chunk chunk, JRPrintHyperlink link)
  {
    if (link != null)
    {
      switch(link.getHyperlinkTypeValue())
      {
        case REFERENCE :
        {
          if (link.getHyperlinkReference() != null)
          {
            switch(link.getHyperlinkTargetValue())
            {
              case BLANK :
              {
                chunk.setAction(
                  PdfAction.javaScript(
                    "if (app.viewerVersion < 7)"
                      + "{this.getURL(\"" + link.getHyperlinkReference() + "\");}"
                      + "else {app.launchURL(\"" + link.getHyperlinkReference() + "\", true);};",
                    pdfWriter
                    )
                  );
                break;
              }
              case SELF :
              default :
              {
                chunk.setAnchor(link.getHyperlinkReference());
                break;
              }
            }
          }
          break;
        }
        case LOCAL_ANCHOR :
        {
          if (link.getHyperlinkAnchor() != null)
          {
            chunk.setLocalGoto(link.getHyperlinkAnchor());
          }
          break;
        }
        case LOCAL_PAGE :
        {
          if (link.getHyperlinkPage() != null)
          {
            chunk.setLocalGoto(JR_PAGE_ANCHOR_PREFIX + reportIndex + "_" + link.getHyperlinkPage().toString());
          }
          break;
        }
        case REMOTE_ANCHOR :
        {
          if (
            link.getHyperlinkReference() != null &&
            link.getHyperlinkAnchor() != null
            )
          {
            chunk.setRemoteGoto(
              link.getHyperlinkReference(),
              link.getHyperlinkAnchor()
              );
          }
          break;
        }
        case REMOTE_PAGE :
        {
          if (
            link.getHyperlinkReference() != null &&
            link.getHyperlinkPage() != null
            )
          {
            chunk.setRemoteGoto(
              link.getHyperlinkReference(),
              link.getHyperlinkPage().intValue()
              );
          }
          break;
        }
        case CUSTOM :
        {
          if (hyperlinkProducerFactory != null)
          {
            String hyperlink = hyperlinkProducerFactory.produceHyperlink(link);
            if (hyperlink != null)
            {
              switch(link.getHyperlinkTargetValue())
              {
                case BLANK :
                {
                  chunk.setAction(
                    PdfAction.javaScript(
                      "if (app.viewerVersion < 7)"
                        + "{this.getURL(\"" + hyperlink + "\");}"
                        + "else {app.launchURL(\"" + hyperlink + "\", true);};",
                      pdfWriter
                      )
                    );
                  break;
                }
                case SELF :
                default :
                {
                  chunk.setAnchor(hyperlink);
                  break;
                }
              }
            }
          }
        }
        case NONE :
        default :
        {
          break;
        }
      }
    }
  }


  /**
   *
   */
  protected Phrase getPhrase(AttributedString as, String text, JRPrintText textElement)
  {
    Phrase phrase = new Phrase();
    int runLimit = 0;

    AttributedCharacterIterator iterator = as.getIterator();
    Locale locale = getTextLocale(textElement);
    
    boolean firstChunk = true;
    while(runLimit < text.length() && (runLimit = iterator.getRunLimit()) <= text.length())
    {
      Map attributes = iterator.getAttributes();
      Chunk chunk = getChunk(attributes, text.substring(iterator.getIndex(), runLimit), locale);
     
      if (firstChunk)
      {
        // only set anchor + bookmark for the first chunk in the text
        setAnchor(chunk, textElement, textElement);
      }
     
      JRPrintHyperlink hyperlink = textElement;
      if (hyperlink.getHyperlinkTypeValue() == HyperlinkTypeEnum.NONE)
      {
        hyperlink = (JRPrintHyperlink)attributes.get(JRTextAttribute.HYPERLINK);
      }
     
      setHyperlinkInfo(chunk, hyperlink);
      phrase.add(chunk);

      iterator.setIndex(runLimit);
      firstChunk = false;
    }

    return phrase;
  }


  /**
   *
   */
  protected Chunk getChunk(Map attributes, String text, Locale locale)
  {
    // underline and strikethrough are set on the chunk below
    Font font = getFont(attributes, locale, false);

    Chunk chunk = new Chunk(text, font);
   
    if (hasUnderline(attributes))
    {
      // using the same values as sun.font.Fond2D
      chunk.setUnderline(null, 0, 1f / 18, 0, -1f / 12, 0);
    }
   
    if (hasStrikethrough(attributes))
    {
      // using the same thickness as sun.font.Fond2D.
      // the position is calculated in Fond2D based on the ascent, defaulting
      // to iText default position which depends on the font size
      chunk.setUnderline(null, 0, 1f / 18, 0, 1f / 3, 0);
    }

    Color backcolor = (Color)attributes.get(TextAttribute.BACKGROUND);
    if (backcolor != null)
    {
      chunk.setBackground(backcolor);
    }

    Object script = attributes.get(TextAttribute.SUPERSCRIPT);
    if (script != null)
    {
      if (TextAttribute.SUPERSCRIPT_SUPER.equals(script))
      {
        chunk.setTextRise(font.getCalculatedLeading(1f)/2);
      }
      else if (TextAttribute.SUPERSCRIPT_SUB.equals(script))
      {
        chunk.setTextRise(-font.getCalculatedLeading(1f)/2);
      }
    }

    if (splitCharacter != null)
    {
      //TODO use line break offsets if available?
      chunk.setSplitCharacter(splitCharacter);
    }

    return chunk;
  }

  protected boolean hasUnderline(Map textAttributes)
  {
    Integer underline = (Integer) textAttributes.get(TextAttribute.UNDERLINE);
    return TextAttribute.UNDERLINE_ON.equals(underline);
  }

  protected boolean hasStrikethrough(Map textAttributes)
  {
    Boolean strike = (Boolean) textAttributes.get(TextAttribute.STRIKETHROUGH);
    return TextAttribute.STRIKETHROUGH_ON.equals(strike);
  }


  /**
   * Creates a PDF font.
   *
   * @param attributes the text attributes of the font
   * @param locale the locale for which to create the font
   * @param setFontLines whether to set underline and strikethrough as font style
   * @return the PDF font for the specified attributes
   */
  protected Font getFont(Map attributes, Locale locale, boolean setFontLines)
  {
    JRFont jrFont = new JRBaseFont(attributes);

    Exception initialException = null;

    Color forecolor = (Color)attributes.get(TextAttribute.FOREGROUND);

    // use the same font scale ratio as in JRStyledText.getAwtAttributedString
    float fontSizeScale = 1f;
    Integer scriptStyle = (Integer) attributes.get(TextAttribute.SUPERSCRIPT);
    if (scriptStyle != null && (
        TextAttribute.SUPERSCRIPT_SUB.equals(scriptStyle)
        || TextAttribute.SUPERSCRIPT_SUPER.equals(scriptStyle)))
    {
      fontSizeScale = 2f / 3;
    }
   
    Font font = null;
    PdfFont pdfFont = null;
    FontKey key = new FontKey(jrFont.getFontName(), jrFont.isBold(), jrFont.isItalic());

    if (fontMap != null && fontMap.containsKey(key))
    {
      pdfFont = (PdfFont) fontMap.get(key);
    }
    else
    {
      FontInfo fontInfo = JRFontUtil.getFontInfo(jrFont.getFontName(), locale);
      if (fontInfo == null)
      {
        //fontName NOT found in font extensions
        pdfFont = new PdfFont(jrFont.getPdfFontName(), jrFont.getPdfEncoding(), jrFont.isPdfEmbedded());
      }
      else
      {
        //fontName found in font extensions
        FontFamily family = fontInfo.getFontFamily();
        FontFace face = fontInfo.getFontFace();
        int faceStyle = java.awt.Font.PLAIN;

        if (face == null)
        {
          //fontName matches family name in font extension
          if (jrFont.isBold() && jrFont.isItalic())
          {
            face = family.getBoldItalicFace();
            faceStyle = java.awt.Font.BOLD | java.awt.Font.ITALIC;
          }
         
          if (face == null && jrFont.isBold())
          {
            face = family.getBoldFace();
            faceStyle = java.awt.Font.BOLD;
          }
         
          if (face == null && jrFont.isItalic())
          {
            face = family.getItalicFace();
            faceStyle = java.awt.Font.ITALIC;
          }
         
          if (face == null)
          {
            face = family.getNormalFace();
            faceStyle = java.awt.Font.PLAIN;
          }
           
//          if (face == null)
//          {
//            throw new JRRuntimeException("Font family '" + family.getName() + "' does not have the normal font face.");
//          }
        }
        else
        {
          //fontName matches face name in font extension; not family name
          faceStyle = fontInfo.getStyle();
        }
       
        String pdfFontName = null;
        int pdfFontStyle = java.awt.Font.PLAIN;
        if (jrFont.isBold() && jrFont.isItalic())
        {
          pdfFontName = family.getBoldItalicPdfFont();
          pdfFontStyle = java.awt.Font.BOLD | java.awt.Font.ITALIC;
        }
       
        if (pdfFontName == null && jrFont.isBold())
        {
          pdfFontName = family.getBoldPdfFont();
          pdfFontStyle = java.awt.Font.BOLD;
        }
       
        if (pdfFontName == null && jrFont.isItalic())
        {
          pdfFontName = family.getItalicPdfFont();
          pdfFontStyle = java.awt.Font.ITALIC;
        }
       
        if (pdfFontName == null)
        {
          pdfFontName = family.getNormalPdfFont();
          pdfFontStyle = java.awt.Font.PLAIN;
        }

        if (pdfFontName == null)
        {
          //in theory, face file cannot be null here
          pdfFontName = (face == null || face.getFile() == null ? jrFont.getPdfFontName() : face.getFile());
          pdfFontStyle = faceStyle;//FIXMEFONT not sure this is correct, in case we inherit pdfFontName from default properties
        }

//        String ttf = face.getFile();
//        if (ttf == null)
//        {
//          throw new JRRuntimeException("The '" + face.getName() + "' font face in family '" + family.getName() + "' returns a null file.");
//        }
       
        pdfFont =
          new PdfFont(
            pdfFontName,
            family.getPdfEncoding() == null ? jrFont.getPdfEncoding() : family.getPdfEncoding(),
             family.isPdfEmbedded() == null ? jrFont.isPdfEmbedded() : family.isPdfEmbedded().booleanValue(),
            jrFont.isBold() && ((pdfFontStyle & java.awt.Font.BOLD) == 0),
            jrFont.isItalic() && ((pdfFontStyle & java.awt.Font.ITALIC) == 0)
            );
      }
    }

    int pdfFontStyle = (pdfFont.isPdfSimulatedBold() ? Font.BOLD : 0)
        | (pdfFont.isPdfSimulatedItalic() ? Font.ITALIC : 0);
    if (setFontLines)
    {
      pdfFontStyle |= (jrFont.isUnderline() ? Font.UNDERLINE : 0)
          | (jrFont.isStrikeThrough() ? Font.STRIKETHRU : 0);
    }
   
    try
    {
      font = FontFactory.getFont(
        pdfFont.getPdfFontName(),
        pdfFont.getPdfEncoding(),
        pdfFont.isPdfEmbedded(),
        jrFont.getFontSize() * fontSizeScale,
        pdfFontStyle,
        forecolor
        );

      // check if FontFactory didn't find the font
      if (font.getBaseFont() == null && font.getFamily() == Font.UNDEFINED)
      {
        font = null;
      }
    }
    catch(Exception e)
    {
      initialException = e;
    }

    if (font == null)
    {
      byte[] bytes = null;

      try
      {
        bytes = JRLoader.loadBytesFromLocation(pdfFont.getPdfFontName(), classLoader, urlHandlerFactory, fileResolver);
      }
      catch(JRException e)
      {
        throw //NOPMD
          new JRRuntimeException(
            "Could not load the following font : "
            + "\npdfFontName   : " + pdfFont.getPdfFontName()
            + "\npdfEncoding   : " + pdfFont.getPdfEncoding()
            + "\nisPdfEmbedded : " + pdfFont.isPdfEmbedded(),
            initialException
            );
      }

      BaseFont baseFont = null;

      try
      {
        baseFont =
          BaseFont.createFont(
            pdfFont.getPdfFontName(),
            pdfFont.getPdfEncoding(),
            pdfFont.isPdfEmbedded(),
            true,
            bytes,
            null
            );
      }
      catch(DocumentException e)
      {
        throw new JRRuntimeException(e);
      }
      catch(IOException e)
      {
        throw new JRRuntimeException(e);
      }

      font =
        new Font(
          baseFont,
          jrFont.getFontSize() * fontSizeScale,
          pdfFontStyle,
          forecolor
          );
    }

    return font;
  }


  /**
   *
   */
  public void exportText(JRPrintText text) throws DocumentException
  {
    PdfTextRenderer textRenderer = new PdfTextRenderer(false);//FIXMETAB optimize this
   
    textRenderer.initialize(this, pdfContentByte, text, getOffsetX(), getOffsetY());
   
    JRStyledText styledText = textRenderer.getStyledText();

    if (styledText == null)
    {
      return;
    }

    int xFillCorrection = 0;
    int yFillCorrection = 0;
    double angle = 0;

    switch (text.getRotationValue())
    {
      case LEFT :
      {
        xFillCorrection = 1;
        angle = Math.PI / 2;
        break;
      }
      case RIGHT :
      {
        yFillCorrection = -1;
        angle = - Math.PI / 2;
        break;
      }
      case UPSIDE_DOWN :
      {
        angle = Math.PI;
        break;
      }
      case NONE :
      default :
      {
      }
    }

    AffineTransform atrans = new AffineTransform();
    atrans.rotate(angle, textRenderer.getX(), jasperPrint.getPageHeight() - textRenderer.getY());
    pdfContentByte.transform(atrans);

    if (text.getModeValue() == ModeEnum.OPAQUE)
    {
      Color backcolor = text.getBackcolor();
      pdfContentByte.setRGBColorFill(
        backcolor.getRed(),
        backcolor.getGreen(),
        backcolor.getBlue()
        );
      pdfContentByte.rectangle(
        textRenderer.getX() + xFillCorrection,
        jasperPrint.getPageHeight() - textRenderer.getY() + yFillCorrection,
        textRenderer.getWidth(),
        - textRenderer.getHeight()
        );
      pdfContentByte.fill();
    }

    if (styledText.length() > 0)
    {
      tagHelper.startText();
     
      /*   */
      textRenderer.render();

      tagHelper.endText();
    }

    atrans = new AffineTransform();
    atrans.rotate(-angle, textRenderer.getX(), jasperPrint.getPageHeight() - textRenderer.getY());
    pdfContentByte.transform(atrans);

    /*   */
    exportBox(
      text.getLineBox(),
      text
      );
  }


  /**
   *
   */
  protected void exportBox(JRLineBox box, JRPrintElement element)
  {
    exportTopPen(box.getTopPen(), box.getLeftPen(), box.getRightPen(), element);
    exportLeftPen(box.getTopPen(), box.getLeftPen(), box.getBottomPen(), element);
    exportBottomPen(box.getLeftPen(), box.getBottomPen(), box.getRightPen(), element);
    exportRightPen(box.getTopPen(), box.getBottomPen(), box.getRightPen(), element);

    pdfContentByte.setLineDash(0f);
    pdfContentByte.setLineCap(PdfContentByte.LINE_CAP_PROJECTING_SQUARE);
  }


  /**
   *
   */
  protected void exportPen(JRPen pen, JRPrintElement element)
  {
    exportTopPen(pen, pen, pen, element);
    exportLeftPen(pen, pen, pen, element);
    exportBottomPen(pen, pen, pen, element);
    exportRightPen(pen, pen, pen, element);

    pdfContentByte.setLineDash(0f);
    pdfContentByte.setLineCap(PdfContentByte.LINE_CAP_PROJECTING_SQUARE);
  }


  /**
   *
   */
  protected void exportTopPen(
    JRPen topPen,
    JRPen leftPen,
    JRPen rightPen,
    JRPrintElement element)
  {
    if (topPen.getLineWidth().floatValue() > 0f)
    {
      float leftOffset = leftPen.getLineWidth().floatValue() / 2 - BorderOffset.getOffset(leftPen);
      float rightOffset = rightPen.getLineWidth().floatValue() / 2 - BorderOffset.getOffset(rightPen);
     
      preparePen(pdfContentByte, topPen, PdfContentByte.LINE_CAP_BUTT);
     
      if (topPen.getLineStyleValue() == LineStyleEnum.DOUBLE)
      {
        float topOffset = topPen.getLineWidth().floatValue();

        pdfContentByte.moveTo(
          element.getX() + getOffsetX() - leftOffset,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() + topOffset / 3
          );
        pdfContentByte.lineTo(
          element.getX() + getOffsetX() + element.getWidth() + rightOffset,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() + topOffset / 3
          );
        pdfContentByte.stroke();

        pdfContentByte.moveTo(
          element.getX() + getOffsetX() + leftOffset / 3,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() - topOffset / 3
          );
        pdfContentByte.lineTo(
          element.getX() + getOffsetX() + element.getWidth() - rightOffset / 3,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() - topOffset / 3
          );
        pdfContentByte.stroke();
      }
      else
      {
        float topOffset =  BorderOffset.getOffset(topPen);
        pdfContentByte.moveTo(
          element.getX() + getOffsetX() - leftOffset,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() - topOffset
          );
        pdfContentByte.lineTo(
          element.getX() + getOffsetX() + element.getWidth() + rightOffset,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() - topOffset
          );
        pdfContentByte.stroke();
      }
    }
  }


  /**
   *
   */
  protected void exportLeftPen(JRPen topPen, JRPen leftPen, JRPen bottomPen, JRPrintElement element)
  {
    if (leftPen.getLineWidth().floatValue() > 0f)
    {
      float topOffset = topPen.getLineWidth().floatValue() / 2 - BorderOffset.getOffset(topPen);
      float bottomOffset = bottomPen.getLineWidth().floatValue() / 2 - BorderOffset.getOffset(bottomPen);

      preparePen(pdfContentByte, leftPen, PdfContentByte.LINE_CAP_BUTT);

      if (leftPen.getLineStyleValue() == LineStyleEnum.DOUBLE)
      {
        float leftOffset = leftPen.getLineWidth().floatValue();

        pdfContentByte.moveTo(
          element.getX() + getOffsetX() - leftOffset / 3,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() + topOffset
          );
        pdfContentByte.lineTo(
          element.getX() + getOffsetX() - leftOffset / 3,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() - element.getHeight() - bottomOffset
          );
        pdfContentByte.stroke();

        pdfContentByte.moveTo(
          element.getX() + getOffsetX() + leftOffset / 3,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() - topOffset / 3
          );
        pdfContentByte.lineTo(
          element.getX() + getOffsetX() + leftOffset / 3,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() - element.getHeight() + bottomOffset / 3
          );
        pdfContentByte.stroke();
      }
      else
      {
        float leftOffset =  BorderOffset.getOffset(leftPen);
        pdfContentByte.moveTo(
          element.getX() + getOffsetX() + leftOffset,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() + topOffset
          );
        pdfContentByte.lineTo(
          element.getX() + getOffsetX() + leftOffset,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() - element.getHeight() - bottomOffset
          );
        pdfContentByte.stroke();
      }
    }
  }


  /**
   *
   */
  protected void exportBottomPen(JRPen leftPen, JRPen bottomPen, JRPen rightPen, JRPrintElement element)
  {
    if (bottomPen.getLineWidth().floatValue() > 0f)
    {
      float leftOffset = leftPen.getLineWidth().floatValue() / 2 - BorderOffset.getOffset(leftPen);
      float rightOffset = rightPen.getLineWidth().floatValue() / 2 - BorderOffset.getOffset(rightPen);
     
      preparePen(pdfContentByte, bottomPen, PdfContentByte.LINE_CAP_BUTT);
     
      if (bottomPen.getLineStyleValue() == LineStyleEnum.DOUBLE)
      {
        float bottomOffset = bottomPen.getLineWidth().floatValue();

        pdfContentByte.moveTo(
          element.getX() + getOffsetX() - leftOffset,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() - element.getHeight() - bottomOffset / 3
          );
        pdfContentByte.lineTo(
          element.getX() + getOffsetX() + element.getWidth() + rightOffset,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() - element.getHeight() - bottomOffset / 3
          );
        pdfContentByte.stroke();

        pdfContentByte.moveTo(
          element.getX() + getOffsetX() + leftOffset / 3,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() - element.getHeight() + bottomOffset / 3
          );
        pdfContentByte.lineTo(
          element.getX() + getOffsetX() + element.getWidth() - rightOffset / 3,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() - element.getHeight() + bottomOffset / 3
          );
        pdfContentByte.stroke();
      }
      else
      {
        float bottomOffset =  BorderOffset.getOffset(bottomPen);
        pdfContentByte.moveTo(
          element.getX() + getOffsetX() - leftOffset,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() - element.getHeight() + bottomOffset
          );
        pdfContentByte.lineTo(
          element.getX() + getOffsetX() + element.getWidth() + rightOffset,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() - element.getHeight() + bottomOffset
          );
        pdfContentByte.stroke();
      }
    }
  }


  /**
   *
   */
  protected void exportRightPen(JRPen topPen, JRPen bottomPen, JRPen rightPen, JRPrintElement element)
  {
    if (rightPen.getLineWidth().floatValue() > 0f)
    {
      float topOffset = topPen.getLineWidth().floatValue() / 2 - BorderOffset.getOffset(topPen);
      float bottomOffset = bottomPen.getLineWidth().floatValue() / 2 - BorderOffset.getOffset(bottomPen);

      preparePen(pdfContentByte, rightPen, PdfContentByte.LINE_CAP_BUTT);

      if (rightPen.getLineStyleValue() == LineStyleEnum.DOUBLE)
      {
        float rightOffset = rightPen.getLineWidth().floatValue();

        pdfContentByte.moveTo(
          element.getX() + getOffsetX() + element.getWidth() + rightOffset / 3,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() + topOffset
          );
        pdfContentByte.lineTo(
          element.getX() + getOffsetX() + element.getWidth() + rightOffset / 3,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() - element.getHeight() - bottomOffset
          );
        pdfContentByte.stroke();

        pdfContentByte.moveTo(
          element.getX() + getOffsetX() + element.getWidth() - rightOffset / 3,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() - topOffset / 3
          );
        pdfContentByte.lineTo(
          element.getX() + getOffsetX() + element.getWidth() - rightOffset / 3,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() - element.getHeight() + bottomOffset / 3
          );
        pdfContentByte.stroke();
      }
      else
      {
        float rightOffset =  BorderOffset.getOffset(rightPen);
        pdfContentByte.moveTo(
          element.getX() + getOffsetX() + element.getWidth() - rightOffset,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() + topOffset
          );
        pdfContentByte.lineTo(
          element.getX() + getOffsetX() + element.getWidth() - rightOffset,
          jasperPrint.getPageHeight() - element.getY() - getOffsetY() - element.getHeight() - bottomOffset
          );
        pdfContentByte.stroke();
      }
    }
  }


  /**
   *
   */
  private static void preparePen(PdfContentByte pdfContentByte, JRPen pen, int lineCap)
  {
    float lineWidth = pen.getLineWidth().floatValue();

    if (lineWidth <= 0)
    {
      return;
    }
   
    pdfContentByte.setLineWidth(lineWidth);
    pdfContentByte.setLineCap(lineCap);

    Color color = pen.getLineColor();
    pdfContentByte.setRGBColorStroke(
      color.getRed(),
      color.getGreen(),
      color.getBlue()
      );

    switch (pen.getLineStyleValue())
    {
      case DOUBLE :
      {
        pdfContentByte.setLineWidth(lineWidth / 3);
        pdfContentByte.setLineDash(0f);
        break;
      }
      case DOTTED :
      {
        switch (lineCap)
        {
          case PdfContentByte.LINE_CAP_BUTT :
          {
            pdfContentByte.setLineDash(lineWidth, lineWidth, 0f);
            break;
          }
          case PdfContentByte.LINE_CAP_PROJECTING_SQUARE :
          {
            pdfContentByte.setLineDash(0, 2 * lineWidth, 0f);
            break;
          }
        }
        break;
      }
      case DASHED :
      {
        switch (lineCap)
        {
          case PdfContentByte.LINE_CAP_BUTT :
          {
            pdfContentByte.setLineDash(5 * lineWidth, 3 * lineWidth, 0f);
            break;
          }
          case PdfContentByte.LINE_CAP_PROJECTING_SQUARE :
          {
            pdfContentByte.setLineDash(4 * lineWidth, 4 * lineWidth, 0f);
            break;
          }
        }
        break;
      }
      case SOLID :
      default :
      {
        pdfContentByte.setLineDash(0f);
        break;
      }
    }
  }


  protected static synchronized void registerFonts ()
  {
    if (!fontsRegistered)
    {
      List fontFiles = JRProperties.getProperties(JRProperties.PDF_FONT_FILES_PREFIX);
      if (!fontFiles.isEmpty())
      {
        for (Iterator i = fontFiles.iterator(); i.hasNext();)
        {
          JRProperties.PropertySuffix font = (JRProperties.PropertySuffix) i.next();
          String file = font.getValue();
          if (file.toLowerCase().endsWith(".ttc"))
          {
            FontFactory.register(file);
          }
          else
          {
            String alias = font.getSuffix();
            FontFactory.register(file, alias);
          }
        }
      }

      List fontDirs = JRProperties.getProperties(JRProperties.PDF_FONT_DIRS_PREFIX);
      if (!fontDirs.isEmpty())
      {
        for (Iterator i = fontDirs.iterator(); i.hasNext();)
        {
          JRProperties.PropertySuffix dir = (JRProperties.PropertySuffix) i.next();
          FontFactory.registerDirectory(dir.getValue());
        }
      }

      fontsRegistered = true;
    }
  }


  static protected class Bookmark
  {
    final PdfOutline pdfOutline;
    final int level;

    Bookmark(Bookmark parent, int x, int top, String title)
    {
      this(parent, new PdfDestination(PdfDestination.XYZ, x, top, 0), title);
    }

    Bookmark(Bookmark parent, PdfDestination destination, String title)
    {
      this.pdfOutline = new PdfOutline(parent.pdfOutline, destination, title, false);
      this.level = parent.level + 1;
    }

    Bookmark(PdfOutline pdfOutline, int level)
    {
      this.pdfOutline = pdfOutline;
      this.level = level;
    }
  }

  static protected class BookmarkStack
  {
    LinkedList stack;

    BookmarkStack()
    {
      stack = new LinkedList();
    }

    void push(Bookmark bookmark)
    {
      stack.add(bookmark);
    }

    Bookmark pop()
    {
      return (Bookmark) stack.removeLast();
    }

    Bookmark peek()
    {
      return (Bookmark) stack.getLast();
    }
  }


  protected void initBookmarks()
  {
    bookmarkStack = new BookmarkStack();

    int rootLevel = isModeBatch && isCreatingBatchModeBookmarks ? -1 : 0;
    Bookmark bookmark = new Bookmark(pdfContentByte.getRootOutline(), rootLevel);
    bookmarkStack.push(bookmark);
  }


  protected void addBookmark(int level, String title, int x, int y)
  {
    Bookmark parent = bookmarkStack.peek();
    // searching for parent
    while(parent.level >= level)
    {
      bookmarkStack.pop();
      parent = bookmarkStack.peek();
    }

    if (!collapseMissingBookmarkLevels)
    {
      // creating empty bookmarks in order to preserve the bookmark level
      for (int i = parent.level + 1; i < level; ++i)
      {
        Bookmark emptyBookmark = new Bookmark(parent, parent.pdfOutline.getPdfDestination(), EMPTY_BOOKMARK_TITLE);
        bookmarkStack.push(emptyBookmark);
        parent = emptyBookmark;
      }
    }

    Bookmark bookmark = new Bookmark(parent, x, jasperPrint.getPageHeight() - y, title);
    bookmarkStack.push(bookmark);
  }


  protected void setAnchor(Chunk chunk, JRPrintAnchor anchor, JRPrintElement element)
  {
    String anchorName = anchor.getAnchorName();
    if (anchorName != null)
    {
      chunk.setLocalDestination(anchorName);

      if (anchor.getBookmarkLevel() != JRAnchor.NO_BOOKMARK)
      {
        addBookmark(anchor.getBookmarkLevel(), anchor.getAnchorName(), element.getX(), element.getY());
      }
    }
  }


  protected void exportFrame(JRPrintFrame frame) throws DocumentException, IOException, JRException
  {
    if (frame.getModeValue() == ModeEnum.OPAQUE)
    {
      int x = frame.getX() + getOffsetX();
      int y = frame.getY() + getOffsetY();

      Color backcolor = frame.getBackcolor();
      pdfContentByte.setRGBColorFill(
        backcolor.getRed(),
        backcolor.getGreen(),
        backcolor.getBlue()
        );
      pdfContentByte.rectangle(
        x,
        jasperPrint.getPageHeight() - y,
        frame.getWidth(),
        - frame.getHeight()
        );
      pdfContentByte.fill();
    }

    setFrameElementsOffset(frame, false);
    try
    {
      exportElements(frame.getElements());
    }
    finally
    {
      restoreElementOffsets();
    }

    exportBox(frame.getLineBox(), frame);
  }


  /**
   * Output stream implementation that discards all the data.
   */
  public static class NullOutputStream extends OutputStream
  {
    public NullOutputStream()
    {
    }

    public void write(int b)
    {
      // discard the data
    }

    public void write(byte[] b, int off, int len)
    {
      // discard the data
    }

    public void write(byte[] b)
    {
      // discard the data
    }
  }


  /**
   *
   */
  class LocalFontMapper implements FontMapper
  {
    public LocalFontMapper()
    {
    }

    public BaseFont awtToPdf(java.awt.Font font)
    {
      // not setting underline and strikethrough as we only need the base font.
      // underline and strikethrough will not work here because PdfGraphics2D
      // doesn't check the font attributes.
      return getFont(font.getAttributes(), null, false).getBaseFont();
    }

    public java.awt.Font pdfToAwt(BaseFont font, int size)
    {
      return null;
    }
  }


  /**
   *
   */
  protected void exportGenericElement(JRGenericPrintElement element)
  {
    GenericElementPdfHandler handler = (GenericElementPdfHandler)
        GenericElementHandlerEnviroment.getHandler(
            element.getGenericType(), PDF_EXPORTER_KEY);
   
    if (handler != null)
    {
      handler.exportElement(exporterContext, element);
    }
    else
    {
      if (log.isDebugEnabled())
      {
        log.debug("No PDF generic element handler for "
            + element.getGenericType());
      }
    }
  }

 
  /**
   *
   */
  protected String getExporterKey()
  {
    return PDF_EXPORTER_KEY;
  }
}
TOP

Related Classes of net.sf.jasperreports.engine.export.JRPdfExporter$Bookmark

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.