com.dlya.facturews.DlyaPdfExporter2.java Source code

Java tutorial

Introduction

Here is the source code for com.dlya.facturews.DlyaPdfExporter2.java

Source

package com.dlya.facturews;
/*
 * JasperReports - Free Java Reporting Library.
 * Copyright (C) 2001 - 2011 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
 */

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.InputStream;
import java.io.OutputStream;
import java.text.AttributedCharacterIterator;
import java.text.AttributedCharacterIterator.Attribute;
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.DefaultJasperReportsContext;
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.JRPropertiesUtil;
import net.sf.jasperreports.engine.JRPropertiesUtil.PropertySuffix;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JasperReportsContext;
import net.sf.jasperreports.engine.Renderable;
import net.sf.jasperreports.engine.RenderableUtil;
import net.sf.jasperreports.engine.base.JRBaseFont;
import net.sf.jasperreports.engine.base.JRBasePrintText;
import net.sf.jasperreports.engine.export.AbstractPdfTextRenderer;
import net.sf.jasperreports.engine.export.FontKey;
import net.sf.jasperreports.engine.export.GenericElementHandlerEnviroment;
import net.sf.jasperreports.engine.export.GenericElementPdfHandler;
import net.sf.jasperreports.engine.export.JRExportProgressMonitor;
import net.sf.jasperreports.engine.export.JRPdfExporter;
import net.sf.jasperreports.engine.export.JRPdfExporterContext;
import net.sf.jasperreports.engine.export.JRPdfExporterParameter;
import net.sf.jasperreports.engine.export.PdfFont;
import net.sf.jasperreports.engine.export.PdfTextRenderer;
import net.sf.jasperreports.engine.export.SimplePdfTextRenderer;
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.fonts.FontUtil;
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.type.RenderableTypeEnum;
import net.sf.jasperreports.engine.util.BreakIteratorSplitCharacter;
import net.sf.jasperreports.engine.util.JRLoader;
import net.sf.jasperreports.engine.util.JRPdfaIccProfileNotFoundException;
import net.sf.jasperreports.engine.util.JRStyledText;
import net.sf.jasperreports.engine.util.JRTextAttribute;
import net.sf.jasperreports.repo.RepositoryUtil;

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.PdfArray;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfDestination;
import com.lowagie.text.pdf.PdfDictionary;
import com.lowagie.text.pdf.PdfName;
import com.lowagie.text.pdf.PdfOutline;
import com.lowagie.text.pdf.PdfString;
import com.lowagie.text.pdf.PdfTemplate;
import com.lowagie.text.pdf.PdfWriter;

/**
 * Efecto de JRPdfExporter EN VERSIN PARA ITEXT 1.2
 * 
 * @author David Torres Palao
 */
public class DlyaPdfExporter2 extends JRPdfExporter {

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

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

    /**
     * Prefix of properties that specify font files for the PDF exporter.
     */
    public static final String PDF_FONT_FILES_PREFIX = PDF_EXPORTER_PROPERTIES_PREFIX + "font.";

    /**
     * Prefix of properties that specify font directories for the PDF exporter.
     */
    public static final String PDF_FONT_DIRS_PREFIX = PDF_EXPORTER_PROPERTIES_PREFIX + "fontdir.";

    /**
     * The exporter key, as used in
     * {@link GenericElementHandlerEnviroment#getHandler(net.sf.jasperreports.engine.JRGenericElementType, String)}.
     */
    public static final String PDF_EXPORTER_KEY = JRPropertiesUtil.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 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<Renderable, com.lowagie.text.Image> loadedImagesMap;
    protected Image pxImage;

    private BookmarkStack bookmarkStack;

    @SuppressWarnings("deprecation")
    private Map<FontKey, PdfFont> pdfFontMap;

    private SplitCharacter splitCharacter;

    protected JRPdfExporterContext exporterContext = new ExporterContext();

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

    /**
     *
     */
    public DlyaPdfExporter2(JasperReportsContext jasperReportsContext) {
        super(jasperReportsContext);
    }

    /**
     *
     */
    protected Image getPxImage() {
        if (pxImage == null) {
            try {
                pxImage = Image.getInstance(
                        JRLoader.loadBytesFromResource("net/sf/jasperreports/engine/images/pixel.GIF"));
            } 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);

            setFontMap();
            setSplitCharacter();
            setHyperlinkProducerFactory();

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

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

            // no export param for this
            collapseMissingBookmarkLevels = JRPropertiesUtil.getInstance(jasperReportsContext).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();
        }
    }

    @SuppressWarnings("deprecation")
    protected void setFontMap() {
        pdfFontMap = (Map<FontKey, PdfFont>) parameters.get(JRExporterParameter.FONT_MAP);
    }

    protected void setSplitCharacter() {
        boolean useFillSplitCharacter;
        Boolean useFillSplitCharacterParam = (Boolean) parameters
                .get(JRPdfExporterParameter.FORCE_LINEBREAK_POLICY);
        if (useFillSplitCharacterParam == null) {
            useFillSplitCharacter = JRPropertiesUtil.getInstance(jasperReportsContext).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);

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

            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() + ")");
            }

            // BEGIN: PDF/A support
            String pdfaConformance = getStringParameter(JRPdfExporterParameter.PDFA_CONFORMANCE,
                    JRPdfExporterParameter.PROPERTY_PDFA_CONFORMANCE);
            boolean gotPdfa = false;
            if (pdfaConformance != null
                    && !JRPdfExporterParameter.PDFA_CONFORMANCE_NONE.equalsIgnoreCase(pdfaConformance)) {
                if (JRPdfExporterParameter.PDFA_CONFORMANCE_1A.equalsIgnoreCase(pdfaConformance)) {
                    //pdfWriter.setPDFXConformance(PdfWriter.PDFA1A);
                    gotPdfa = true;
                } else if (JRPdfExporterParameter.PDFA_CONFORMANCE_1B.equalsIgnoreCase(pdfaConformance)) {
                    //pdfWriter.setPDFXConformance(PdfWriter.PDFA1B);
                    gotPdfa = true;
                }
            }

            if (gotPdfa) {

                //pdfWriter.createXmpMetadata();
            } else {
                //pdfWriter.setRgbTransparencyBlending(true);
            }
            // END: PDF/A support

            document.open();

            // BEGIN: PDF/A support
            if (gotPdfa) {
                String iccProfilePath = getStringParameter(JRPdfExporterParameter.PDFA_ICC_PROFILE_PATH,
                        JRPdfExporterParameter.PROPERTY_PDFA_ICC_PROFILE_PATH);
                if (iccProfilePath != null) {
                    PdfDictionary pdfDictionary = new PdfDictionary(PdfName.OUTPUTINTENT);
                    pdfDictionary.put(PdfName.OUTPUTCONDITIONIDENTIFIER, new PdfString("sRGB IEC61966-2.1"));
                    pdfDictionary.put(PdfName.INFO, new PdfString("sRGB IEC61966-2.1"));
                    //pdfDictionary.put(PdfName.S, PdfName.GTS_PDFA1);

                    InputStream iccIs = RepositoryUtil.getInstance(jasperReportsContext)
                            .getInputStreamFromLocation(iccProfilePath);
                    //PdfICCBased pdfICCBased = new PdfICCBased(ICC_Profile.getInstance(iccIs));
                    //pdfICCBased.remove(PdfName.ALTERNATE);
                    //pdfDictionary.put(PdfName.DESTOUTPUTPROFILE, pdfWriter.addToBody(pdfICCBased).getIndirectReference());

                    pdfWriter.getExtraCatalog().put(PdfName.OUTPUTINTENTS, new PdfArray(pdfDictionary));
                } else {
                    throw new JRPdfaIccProfileNotFoundException();
                }
            }
            // END: PDF/A support

            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(jasperPrintList.get(reportIndex));
                loadedImagesMap = new HashMap<Renderable, com.lowagie.text.Image>();

                setPageSize(null);

                BorderOffset.setLegacy(JRPropertiesUtil.getInstance(jasperReportsContext)
                        .getBooleanProperty(jasperPrint, BorderOffset.PROPERTY_LEGACY_BORDER_OFFSET, false));

                boolean sizePageToContent = JRPropertiesUtil.getInstance(jasperReportsContext).getBooleanProperty(
                        jasperPrint, JRPdfExporterParameter.PROPERTY_SIZE_PAGE_TO_CONTENT, false);

                List<JRPrintPage> 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 = pages.get(pageIndex);

                        if (sizePageToContent) {
                            setPageSize(page);
                        }

                        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<Attribute, Object> attributes = new HashMap<Attribute, Object>();
        FontUtil.getInstance(jasperReportsContext).getAttributesWithoutAwtFont(attributes,
                new JRBasePrintText(jasperPrint.getDefaultStyleProvider()));
        Font pdfFont = getFont(attributes, getLocale(), false);
        Chunk 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 void setPageSize(JRPrintPage page) throws JRException, DocumentException, IOException {
        int pageWidth = jasperPrint.getPageWidth();
        int pageHeight = jasperPrint.getPageHeight();

        if (page != null) {
            Collection<JRPrintElement> elements = page.getElements();
            for (JRPrintElement element : elements) {
                int elementRight = element.getX() + element.getWidth();
                int elementBottom = element.getY() + element.getHeight();
                pageWidth = pageWidth < elementRight ? elementRight : pageWidth;
                pageHeight = pageHeight < elementBottom ? elementBottom : pageHeight;
            }

            pageWidth += jasperPrint.getRightMargin();
            pageHeight += jasperPrint.getBottomMargin();
        }

        Rectangle pageSize;
        switch (jasperPrint.getOrientationValue()) {
        case LANDSCAPE:
            // using rotate to indicate landscape page
            pageSize = new Rectangle(pageHeight, pageWidth).rotate();
            break;
        default:
            pageSize = new Rectangle(pageWidth, pageHeight);
            break;
        }
        document.setPageSize(pageSize);
    }

    /**
     *
     */
    protected void exportPage(JRPrintPage page) throws JRException, DocumentException, IOException {
        //tagHelper.startPage();

        Collection<JRPrintElement> elements = page.getElements();
        exportElements(elements);

        //tagHelper.endPage();

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

    protected void exportElements(Collection<JRPrintElement> elements)
            throws DocumentException, IOException, JRException {
        if (elements != null && elements.size() > 0) {
            for (Iterator<JRPrintElement> it = elements.iterator(); it.hasNext();) {
                JRPrintElement element = 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);
    }

    /**
     *
     */
    public 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;

        Renderable renderer = printImage.getRenderable();

        if (renderer != null && availableImageWidth > 0 && availableImageHeight > 0) {
            if (renderer.getTypeValue() == RenderableTypeEnum.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 = RenderableUtil.getInstance(jasperReportsContext).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.getTypeValue() == RenderableTypeEnum.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 = RenderableUtil.getInstance(jasperReportsContext)
                            .getOnErrorRendererForDimension(renderer, printImage.getOnErrorTypeValue());
                    if (renderer == null) {
                        break;
                    }

                    int normalWidth = availableImageWidth;
                    int normalHeight = availableImageHeight;

                    Dimension2D dimension = renderer.getDimension(jasperReportsContext);
                    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(jasperReportsContext, 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 = loadedImagesMap.get(renderer);
                    } else {
                        try {
                            image = com.lowagie.text.Image.getInstance(renderer.getImageData(jasperReportsContext));
                            imageTesterPdfContentByte.addImage(image, 10, 0, 0, 10, 0, 0);
                        } catch (Exception e) {
                            JRImageRenderer tmpRenderer = JRImageRenderer.getOnErrorRendererForImage(
                                    jasperReportsContext,
                                    JRImageRenderer.getInstance(renderer.getImageData(jasperReportsContext)),
                                    printImage.getOnErrorTypeValue());
                            if (tmpRenderer == null) {
                                break;
                            }
                            java.awt.Image awtImage = tmpRenderer.getImage(jasperReportsContext);
                            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 = loadedImagesMap.get(renderer);
                    } else {
                        try {
                            image = com.lowagie.text.Image.getInstance(renderer.getImageData(jasperReportsContext));
                            imageTesterPdfContentByte.addImage(image, 10, 0, 0, 10, 0, 0);
                        } catch (Exception e) {
                            JRImageRenderer tmpRenderer = JRImageRenderer.getOnErrorRendererForImage(
                                    jasperReportsContext,
                                    JRImageRenderer.getInstance(renderer.getImageData(jasperReportsContext)),
                                    printImage.getOnErrorTypeValue());
                            if (tmpRenderer == null) {
                                break;
                            }
                            java.awt.Image awtImage = tmpRenderer.getImage(jasperReportsContext);
                            image = com.lowagie.text.Image.getInstance(awtImage, null);
                        }

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

                    image.scaleToFit(availableImageWidth, availableImageHeight);

                    xoffset = (int) (xalignFactor * (availableImageWidth - image.plainWidth()));
                    yoffset = (int) (yalignFactor * (availableImageHeight - image.plainHeight()));

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

                    break;
                }
                }

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

                    scaledWidth = image.scaledWidth();
                    scaledHeight = image.scaledHeight();
                }
            } 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(jasperReportsContext);
                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(jasperReportsContext, 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<Attribute, Object> 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<Attribute, Object> 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.getCalculatedSize() / 2);
            } else if (TextAttribute.SUPERSCRIPT_SUB.equals(script)) {
                chunk.setTextRise(-font.getCalculatedSize() / 2);
            }
        }

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

        return chunk;
    }

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

    protected boolean hasStrikethrough(Map<Attribute, Object> 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
     */
    @SuppressWarnings("deprecation")
    protected Font getFont(Map<Attribute, Object> 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 = pdfFontMap.get(key);
        } else {
            FontInfo fontInfo = FontUtil.getInstance(jasperReportsContext).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.family() == Font.UNDEFINED) {
                font = null;
            }
        } catch (Exception e) {
            initialException = e;
        }

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

            try {
                bytes = RepositoryUtil.getInstance(jasperReportsContext)
                        .getBytesFromLocation(pdfFont.getPdfFontName());
            } 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 {
        AbstractPdfTextRenderer textRenderer = text.getLeadingOffset() == 0
                ? new PdfTextRenderer(jasperReportsContext,
                        getPropertiesUtil().getBooleanProperty(JRStyledText.PROPERTY_AWT_IGNORE_MISSING_FONT))
                : new SimplePdfTextRenderer(jasperReportsContext,
                        getPropertiesUtil().getBooleanProperty(JRStyledText.PROPERTY_AWT_IGNORE_MISSING_FONT)//FIXMECONTEXT replace with getPropertiesUtil in all exporters
                );//FIXMETAB optimize this

        textRenderer.initialize(this, pdfContentByte, text, getOffsetX(), getOffsetY());

        JRStyledText styledText = textRenderer.getStyledText();

        if (styledText == null) {
            return;
        }

        double angle = 0;

        switch (text.getRotationValue()) {
        case LEFT: {
            angle = Math.PI / 2;
            break;
        }
        case RIGHT: {
            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(), jasperPrint.getPageHeight() - textRenderer.getY(),
                    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<PropertySuffix> fontFiles = JRPropertiesUtil.getInstance(DefaultJasperReportsContext.getInstance())
                    .getProperties(PDF_FONT_FILES_PREFIX);//FIXMECONTEXT no default here and below
            if (!fontFiles.isEmpty()) {
                for (Iterator<PropertySuffix> i = fontFiles.iterator(); i.hasNext();) {
                    JRPropertiesUtil.PropertySuffix font = i.next();
                    String file = font.getValue();
                    if (file.toLowerCase().endsWith(".ttc")) {
                        FontFactory.register(file);
                    } else {
                        String alias = font.getSuffix();
                        FontFactory.register(file, alias);
                    }
                }
            }

            List<PropertySuffix> fontDirs = JRPropertiesUtil.getInstance(DefaultJasperReportsContext.getInstance())
                    .getProperties(PDF_FONT_DIRS_PREFIX);
            if (!fontDirs.isEmpty()) {
                for (Iterator<PropertySuffix> i = fontDirs.iterator(); i.hasNext();) {
                    JRPropertiesUtil.PropertySuffix dir = 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<Bookmark> stack;

        BookmarkStack() {
            stack = new LinkedList<Bookmark>();
        }

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

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

        Bookmark peek() {
            return 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.
            Map<Attribute, Object> atts = new HashMap<Attribute, Object>();
            atts.putAll(font.getAttributes());
            return getFont(atts, null, false).getBaseFont();
        }

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

    /**
     *
     */
    protected void exportGenericElement(JRGenericPrintElement element) {
        GenericElementPdfHandler handler = (GenericElementPdfHandler) GenericElementHandlerEnviroment
                .getInstance(getJasperReportsContext())
                .getElementHandler(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;
    }
}