Hot questions for Using JasperReports in itext

Question:

When a PDF is exported from Crystal Reports, the bookmarks panel shows by default when the PDF is opened; however, when using JasperReports, the bookmark panel is not opened by default and has to be opened manually.

How can JasperReports export a PDF that opens with bookmarks shown by default?


Answer:

AFIK there is no configuration in jasper-report to set view preference (page mode). My only solution would be to post elaborate the pdf with itext (library used to export to pdf, already in classpath)

Example

We will export jasper as a PDF to a memory stream (ByteArrayOutputStream) then use itext's PdfStamper to add viewer preferences PageModeUseOutlines1

//Get the JasperPrint object (exact code to achieve this intentional left out since command depends on application)
JasperPrint jasperPrint = JasperFillManager.fillReport(...); 

//Export to pdf into a memory stream
JRPdfExporter exporter = new JRPdfExporter();
exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
ByteArrayOutputStream memoryStream = new ByteArrayOutputStream();
exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(memoryStream));
SimplePdfExporterConfiguration configuration = new SimplePdfExporterConfiguration();
exporter.setConfiguration(configuration);
exporter.exportReport();

//Use stamper to set viewer prederence 
PdfReader pdfReader = new PdfReader(new ByteArrayInputStream(memoryStream.toByteArray()));
PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileOutputStream("my.pdf"));          
pdfStamper.getWriter().setViewerPreferences(PdfWriter.PageModeUseOutlines);
pdfStamper.close();
pdfReader.close();

1. Link is to itext5 api, but note that jasper-reports actually uses a special version of itext 2.1.7 see maven dependence for more information

Question:

I created a jasper report and now I need to export that report to pdf format. This is my code for that.

        // compiles jrxml
        JasperCompileManager.compileReportToFile(reportName + ".jrxml");
        // fills compiled report with parameters and a connection
        JasperPrint print = JasperFillManager.fillReport(reportName + ".jasper", parameters, connection);

        // to view the report
        //JasperViewer.viewReport(print, false);

        // export repor to pdf
        JasperExportManager.exportReportToPdfFile(print, "fromXml.pdf");

When I view the report using JasperViewer, it works fine. But When I exporting the report into pdf format, it gives me the following exception.

Exception in thread "main" java.lang.NoClassDefFoundError: com/itextpdf/text/DocumentException

But I have put the iText jar into my libraries.

But one thing, I had another version of iText jar before. It gave me the same exception. I thought that there may be some issues with the version of it.(I followed a tutorial, so I added the same jar as they had used in the tutorial replacing the new jar I had used).

but still the problem remains. I thought this may be the issue now(the accepted answer of the question). But I do not know how to fix it. I removed the entire library and added it again with iText jar which had been used by the tutorial. But I had no luck.

Could you please help me to overcome this issue. Thank you!


Answer:

You need to upgrade your iText-2.1.7.jar file to latest version as earlier the package that was used by jar is com.lowagie...

See the change list here which says it has changed the package name.

Question:

I've been using JasperReports in my java projects for a while, but now i'm facing a problem. A customer requires the generation of PDF files compliant with the ISO 32000-2:2017 standard. JasperReports uses iText for pdf rendering, and I know that the latest version of iText can handle PDF 2.0, but i've found out that JasperReports is currently using an older version of iText because of IP issues with the newer releases. Does it mean that i cannot create PDF2.0-compatible files with this library? Do you have any alternative?

Thanks in advance


Answer:

No, JasperReports doesn't support PDF 2.0. JasperReports can't use iText 7.1.x because (1.) its open source license isn't compatible with iText's open source license, and (2.) as far as I know, TIBCO isn't a paying customer of iText Group.

As an alternative, you might look into iText DITO. DITO stands for Design Interactive Templates Online. It's a new product from iText Group that will be released soon, but if you contact iText, you might get early access.

The goal is to allow people to create two types of templates using a WYSIWYG tool.

  • The first type consists of a template for input. When deployed in combination with the DITO SDK, it produces HTML 5 forms that can be used to enter data manually.
  • The second type consists of a template for output. When deployed in combination with the DITO SDK, it produces PDF documents (PDF 2.0, PDF/A, PDF/UA,...).

The template itself is a .dito file. That file is a ZIP file containing HTML, CSS, resources such as images, and information about data binding between the variable fields in your template and your actual data (e.g. data stored in a JSON file).

The DITO SDK takes the HTML template, populates it with data based on the data binding, and then uses iText 7 and the pdfHTML add-on to create the PDF.

Currently, I don't know of any other vendor that has a templating solution that supports PDF 2.0.

Question:

I have created two report jrxml with jasper report. In my java program I merge the two report into one PDF with iText. The problem is that the pdf contains only one report plus a blank page. I have also done this proof: in my java program creation report one creation report two, merge and I see pdf containig only report one plus blank page in my java program creation report two and then creation report one, merge and I see pdf containing only report two plus blank page

I have to obtain the pdf with both reports Does someone help me to solve the problem?

Thanks in advance

Attach the code of my java program:

@Name("pdfFactory")
public class PdfScalareFactory {

    private static final String JASPER_FILE_MOVIMENTI = "scalarePdf/pdf_movimenti.jrxml";
    private static final String JASPER_FILE_SCALARE = "scalarePdf/pdf_scalare.jrxml";

    @SuppressWarnings("rawtypes")
    public byte[] rawPdf(BeScalare beScalare, String codTabulato, String output) throws JRException, IOException {

        JRBeanArrayDataSource dataSource = new JRBeanArrayDataSource(new Object[]{beScalare}); 

        //report's list
        List<byte[]> pdfFilesAsByteArray = new ArrayList<byte[]>();

        //Report one
        Class cScalare = this.getClass();
        ClassLoader clScalare = cScalare.getClassLoader();
        InputStream isScalare = clScalare.getResourceAsStream(JASPER_FILE_SCALARE);
        JasperDesign jasDesignScalare = JRXmlLoader.load(isScalare);
        //compile report one
        JasperReport reportScalare = JasperCompileManager.compileReport(jasDesignScalare);      
        //parameters report one
        Map<String, Object> paramScalare = new HashMap<String, Object>();
        JRBeanCollectionDataSource itemsScalareSaldiPerValuta = new JRBeanCollectionDataSource(beScalare.getLstBeScalareSaldiPerValuta());
        paramScalare.put("scalareSaldiPerValuta", itemsScalareSaldiPerValuta);
        //fill report one
        JasperPrint jasperPrintScalare = JasperFillManager.fillReport(reportScalare, paramScalare, dataSource);
        pdfFilesAsByteArray.add(JasperExportManager.exportReportToPdf(jasperPrintScalare));

        //Report two
        Class c = this.getClass();
        ClassLoader cl = c.getClassLoader();
        InputStream is = cl.getResourceAsStream(JASPER_FILE_MOVIMENTI);
        JasperDesign jasDesign = JRXmlLoader.load(is);
        //compile report two
        JasperReport reportMovimenti = JasperCompileManager.compileReport(jasDesign);
        //parameters report two
        Map<String, Object> paramMovimenti = new HashMap<String, Object>();
        BufferedImage imgNumeroVerde = ImageIO.read(getClass().getResource("/scalarePdf/headphones.png"));
        paramMovimenti.put("numeroVerde", imgNumeroVerde);
        BufferedImage imgInternet = ImageIO.read(getClass().getResource("/scalarePdf/internet.png"));
        paramMovimenti.put("internet", imgInternet);        
        JRBeanCollectionDataSource itemsScalareMovimenti = new JRBeanCollectionDataSource(beScalare.getLstBeScalareMovimenti());
        paramMovimenti.put("scalareMovimenti", itemsScalareMovimenti);
        //fill report two
        JasperPrint jasperPrintMovimenti = JasperFillManager.fillReport(reportMovimenti, paramMovimenti, dataSource);
        pdfFilesAsByteArray.add(JasperExportManager.exportReportToPdf(jasperPrintMovimenti));

        //merge the two reports in one
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        Document document = null;
        PdfCopy writer = null;
        for (byte[] pdfByteArray : pdfFilesAsByteArray) {

            try {
                PdfReader reader = new PdfReader(pdfByteArray);
                int numberOfPages = reader.getNumberOfPages();

                if (document == null) {
                    document = new Document(reader.getPageSizeWithRotation(1));
                    writer = new PdfCopy(document, outStream); // new
                    document.open();
                }
                PdfImportedPage page;
                for (int i = 0; i < numberOfPages;) {
                    ++i;
                    page = writer.getImportedPage(reader, i);
                    writer.addPage(page);
                }
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
        document.close();
        outStream.close();
        return outStream.toByteArray();
    }
}

Answer:

Problem is in the data source and not in merging. You create one data source for both reports but the first report will consume it and then the second report has data source with pointer at the end.

You use JRBeanArrayDataSource which implements JRRewindableDataSource so you can call the moveFirst() method to return data source pointer on the first position:

//Report two
dataSource.moveFirst();

Or you can create the data source again for the second report:

//Report two
dataSource = new JRBeanArrayDataSource(new Object[]{beScalare}); 

Note: But as Amongalen mentioned in the comment it is easier to merge two and more Jasper reports using JRPdfExporter and List<JasperPrint> instance as an input.

Question:

In jasper report I'm using anchor's with hyperlinkTooltipExpression

Example code:

 <textField hyperlinkType="LocalAnchor">
            <reportElement x="267" y="94" width="100" height="30" uuid="8fa9ce3d-015c-4d13-a677-3b9dbea4c222"/>
            <textFieldExpression><![CDATA["Anchor Target"]]></textFieldExpression>
            <hyperlinkAnchorExpression><![CDATA["expert"]]></hyperlinkAnchorExpression>
            <hyperlinkTooltipExpression><![CDATA["expert"]]></hyperlinkTooltipExpression>
        </textField>

This works in IDE preview, but if exporting to PDF the tooltip's are not displayed in Adobe Reader, in Document viewer (linux) they are displayed but with es. "Go To Page x"


Answer:

In jasper report I can not find any support for generating tooltip's on hyper links, JasperReport source code.

However you can override the setHyperlinkInfo on the JRPdfExporter generating the annotation manually.

Example on how to generate tooltip on LocalAnchor:

JRPdfExporter exporter = new JRPdfExporter() {
    @Override
    protected void setHyperlinkInfo(Chunk chunk, JRPrintHyperlink link) {
        if (link!=null && link.getHyperlinkTooltip() != null && HyperlinkTypeEnum.LOCAL_ANCHOR.equals(link.getHyperlinkTypeValue())) {
            PdfFormField pushButton = PdfFormField.createPushButton(pdfWriter);
            pushButton.setFieldName(String.format("tt%s", chunk.hashCode()));//Need's unique name if multiple
            Rectangle rect = new Rectangle(0, 0, 0, 0);
            pushButton.setWidget(rect, PdfAnnotation.HIGHLIGHT_NONE);
            pushButton.put(PdfName.TU, new PdfString(link.getHyperlinkTooltip(), PdfObject.TEXT_UNICODE));
            chunk.setAnnotation(pushButton);
            if (link.getHyperlinkAnchor() != null){                         
               pushButton.setAction(PdfAction.gotoLocalPage(link.getHyperlinkAnchor(), false));
            }
            return;         
        }
        super.setHyperlinkInfo(chunk, link);
    }
};

To generate tooltip on other type of hyperlinkType (Reference, Remote ecc), continue the implementation in similar matter.