Hot questions for Using JasperReports in pdf generation

Question:

Generating PDF/A in jasper-report, contains numerous pitfalls and is not supported in some versions of jasper-report. This is why I have decided to pass this Question-Answer post, indicating the steps and library version necessary to export a simple report with a graph to PDF/A

Sample data (usersRep.csv)

+----------------+--------+
|      User      |  Rep   |
+----------------+--------+
| Jon Skeet      | 854503 |
| Darin Dimitrov | 652133 |
| BalusC         | 639753 |
| Hans Passant   | 616871 |
| Me             |   5640 |
+----------------+--------+

Sample jrxml (reputation.jrxml)

<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="reputation" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="a88bd694-4f90-41fc-84d0-002b90b2d73e">
    <queryString>
        <![CDATA[]]>
    </queryString>
    <field name="User" class="java.lang.String"/>
    <field name="Rep" class="java.lang.Long"/>
    <columnHeader>
        <band height="20" splitType="Stretch">
            <staticText>
                <reportElement x="0" y="0" width="100" height="20" uuid="9e7b5f50-5795-4c95-a122-f14f2e3f9366"/>
                <box leftPadding="3" bottomPadding="0" rightPadding="3">
                    <pen lineWidth="0.25"/>
                    <topPen lineWidth="0.25"/>
                    <leftPen lineWidth="0.25"/>
                    <bottomPen lineWidth="0.5" lineStyle="Double"/>
                    <rightPen lineWidth="0.25"/>
                </box>
                <textElement verticalAlignment="Middle">
                    <font fontName="SansSerif" isBold="true"/>
                </textElement>
                <text><![CDATA[User]]></text>
            </staticText>
            <staticText>
                <reportElement x="100" y="0" width="100" height="20" uuid="4a6f0a2a-d9b5-4e74-a9e8-0f965336f2bf"/>
                <box leftPadding="3" bottomPadding="0" rightPadding="3">
                    <pen lineWidth="0.25"/>
                    <topPen lineWidth="0.25"/>
                    <leftPen lineWidth="0.25"/>
                    <bottomPen lineWidth="0.5" lineStyle="Double"/>
                    <rightPen lineWidth="0.25"/>
                </box>
                <textElement textAlignment="Right" verticalAlignment="Middle">
                    <font fontName="SansSerif" isBold="true"/>
                </textElement>
                <text><![CDATA[Reputation]]></text>
            </staticText>
        </band>
    </columnHeader>
    <detail>
        <band height="20" splitType="Stretch">
            <textField>
                <reportElement x="0" y="0" width="100" height="20" uuid="8ff583b9-88dc-4726-85e1-16d79de78095"/>
                <box leftPadding="3" bottomPadding="0" rightPadding="3">
                    <pen lineWidth="0.25"/>
                    <topPen lineWidth="0.25"/>
                    <leftPen lineWidth="0.25"/>
                    <bottomPen lineWidth="0.25"/>
                    <rightPen lineWidth="0.25"/>
                </box>
                <textElement verticalAlignment="Middle">
                    <font fontName="SansSerif"/>
                </textElement>
                <textFieldExpression><![CDATA[$F{User}]]></textFieldExpression>
            </textField>
            <textField>
                <reportElement x="100" y="0" width="100" height="20" uuid="ebd33b2f-7297-41c2-9dc7-78ff472890c4"/>
                <box leftPadding="3" bottomPadding="0" rightPadding="3">
                    <pen lineWidth="0.25"/>
                    <topPen lineWidth="0.25"/>
                    <leftPen lineWidth="0.25"/>
                    <bottomPen lineWidth="0.25"/>
                    <rightPen lineWidth="0.25"/>
                </box>
                <textElement textAlignment="Right" verticalAlignment="Middle">
                    <font fontName="SansSerif"/>
                </textElement>
                <textFieldExpression><![CDATA[$F{Rep}]]></textFieldExpression>
            </textField>
        </band>
    </detail>
    <pageFooter>
        <band height="140">
            <pieChart>
                <chart isShowLegend="false">
                    <reportElement x="225" y="-670" width="320" height="140" uuid="23bd26a6-04a4-406f-8a1a-5e1b260cb75d"/>
                    <chartTitle/>
                    <chartSubtitle/>
                    <chartLegend/>
                </chart>
                <pieDataset>
                    <keyExpression><![CDATA[$F{User}]]></keyExpression>
                    <valueExpression><![CDATA[$F{Rep}]]></valueExpression>
                </pieDataset>
                <piePlot>
                    <plot/>
                    <itemLabel/>
                </piePlot>
            </pieChart>
        </band>
    </pageFooter>
</jasperReport>

Java code to export to PDF (reputation.pdf)

JasperReport report = JasperCompileManager.compileReport("reputation.jrxml");
JRCsvDataSource datasource = new JRCsvDataSource("usersRep.csv");
datasource.setFieldDelimiter(';');
datasource.setUseFirstRowAsHeader(true);

JasperPrint jasperPrint = JasperFillManager.fillReport(report, new HashMap<String, Object>(),datasource);

JRPdfExporter exporter = new JRPdfExporter();
exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
exporter.setExporterOutput(new SimpleOutputStreamExporterOutput("reputation.pdf"));
SimplePdfExporterConfiguration configuration = new SimplePdfExporterConfiguration();
configuration.setMetadataAuthor("Me and only me");
exporter.setConfiguration(configuration);
exporter.exportReport();

This export the report to a pdf, what do I need to do to generate a PDF/A-1a?


Answer:

JasperReports Library 4.1.2.3 or higher is needed (with discontinued support in 6.0.0 see NullPointerException at the end).

These steps are need to generate a PDF/A, they can be achieved both by java code or by setting jrxml property to root tag (jasper-server support). I will show both but only one method is necessary.

Set PDF/A Conformance

java

configuration.setPdfaConformance(PdfaConformanceEnum.PDFA_1A); // or PdfaConformanceEnum.PDFA_1B

jrxml

<property name="net.sf.jasperreports.export.pdfa.conformance" value="pdfa1a" />
Set ICC Profile

to avoid JRPdfaIccProfileNotFoundException: The ICC profile is not available to the JVM

java

configuration.setIccProfilePath("srgb.icc");

jrxml

<property name="net.sf.jasperreports.export.pdfa.icc.profile.path" value="srgb.icc" />
Embed all font used in reports, using font-extensions

If you still have error

com.lowagie.text.pdf.PdfXConformanceException: All the fonts must be embedded. This one isn't: Helvetica

include a default style in the jrxml indicating fontName that is included in font extension, example

<style name="default" isDefault="true" fontName="DejaVu Sans"/>
Remove transparent objects and layers (Optional Content Groups) they are not allowed

to avoid PdfXConformanceException: Transparency is not allowed

In example the chart element must be Opaque and to avoid transparency on the labels you can implement a JRChartCustomizer

public class NoTransparencyCustomizer implements JRChartCustomizer{
    @Override
    public void customize(JFreeChart chart, JRChart jrchart) {
        PiePlot plot = (PiePlot) chart.getPlot();
        plot.setLabelShadowPaint(Color.GRAY);       
    }
}
Set Tagged and tag language (unnecessary for PDF/A-1b)

java

configuration.setTagged(true);
configuration.setTagLanguage("en-us");

jrxml

<property name="net.sf.jasperreports.export.pdf.tagged" value="true" />
<property name="net.sf.jasperreports.export.pdf.tag.language" value="en-us"/>
Result

This is the result implementing the above, switching fontName to DejaVu Sans and using the bundled jasperreports-fonts.jar as font-extension. It has been validated successfully on pdf-tools for both PDF/A-1a and PDF/A-1b

No piece of the cake for me

Discontinued support In jasper report version 6.0.0 a NullPointerException at com.itextpdf.text.pdf.internal.PdfA1Checker.checkPdfObject was always thrown. This has been solved in 6.0.4 and above see Jasper report tracker.

Question:

I read

and I expected that Jasper Reports throws a JRFontNotFoundException if a font is not installed on the machine.

But my application doesn't throw a JRFontNotFoundException, although I have not installed the used font "Helvetica" on any (Jaspersoft Studios, JasperReports, Adobe Acrobat Reader) machine.

JRXML:

<parameter name="timestamp" class="java.util.Date"/>
[...]
<textField>
    <reportElement x="0" y="0" width="50" height="16" uuid="0007846a-26f1-457a-a198-67a2f7c8417c">
        <property name="local_mesure_unitwidth" value="pixel"/>
        <property name="com.jaspersoft.studio.unit.width" value="px"/>
        <property name="local_mesure_unitx" value="pixel"/>
        <property name="com.jaspersoft.studio.unit.x" value="px"/>
        <property name="local_mesure_unity" value="pixel"/>
        <property name="com.jaspersoft.studio.unit.y" value="px"/>
        <property name="local_mesure_unitheight" value="pixel"/>
        <property name="com.jaspersoft.studio.unit.height" value="px"/>
    </reportElement>
    <box padding="2"/>
    <textElement textAlignment="Left" verticalAlignment="Top">
        <font size="8" pdfFontName="Helvetica" pdfEncoding="Cp1250" isPdfEmbedded="true"/>
    </textElement>
    <textFieldExpression><![CDATA[DATEFORMAT($P{timestamp},"dd.MM HH:mm")]]></textFieldExpression>
</textField>

Maven dependencies:

<dependency>
    <groupId>net.sf.jasperreports</groupId>
    <artifactId>jasperreports</artifactId>
    <version>5.6.0</version>
</dependency>
<dependency>
    <groupId>net.sf.jasperreports</groupId>
    <artifactId>jasperreports-functions</artifactId>
    <version>5.6.0</version>
</dependency>

Java:

private byte[] createPdf() {

    try {
        InputStream is = getClass().getResourceAsStream("MyReport.jasper");
        JasperReport jasperReport = (JasperReport) JRLoader.loadObject(is);
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("timestamp", new Date());
        JRDataSource jrDataSource = new JRBeanCollectionDataSource(new Vector(), false);
        JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, jrDataSource);
        byte[] pdf = JasperExportManager.exportReportToPdf(jasperPrint);
        return pdf;
    } catch (JRException e) {
        throw new RuntimeException("Could not create PDF.", e);
    }
}

The reason is that JRFontNotFoundException is only thrown if font in the attribute fontName is not installed:

Exception raised when a font name used as value for the fontName attribute in the report template, is not found in any of the runtime available JasperReports font extensions, nor among the font names available to the Java Virtual Machine.

Is there any way to abort generation of PDF if the font in attribute pdfFontName is not installed (instead of using any other installed font)?


Answer:

Your are setting pdfFontName not fontName

pdfFontName was an old way, now deprecated to indicate what font the itext library should use, jasper-reports will not throw JRFontNotFoundException if the font is missing, instead itext will throw an exception that is caught and relaunched as a JRRuntimeException.

In itext Helvetica is included as afm file, hence itext will not throw any exception if this is used, however this does not guarantee that your text is rendered correctly if you are indicating another font (in your case not indicating = default font) in jasper-reports. In fact this is a mess and both pdfFontName and pfdEncoding was deprecated.

Is there any way to abort generation of PDF if the font in attribute pdfFontName is not installed?

Don't use pdfFontName, but if you insist (for the sake of the question) then also set fontName="Helvetica", setting the jasper-reports font will raise a JRFontNotFoundException if not found.

The correct way is to only set the fontName and then provide font-extensions, in the font-extension you include the actual ttf, indicate the encoding and if it should be embedded.

BTW: I would use encoding Identity-H this is recommend for newer PDF standards and gives you the ability to mix different encoding.

Question:

I have a problem with an accounting web application that was recently enhanced with printing using Jasper Reports. Being an accounting application, it has been decided that it is being deployed to the accountant's workstation which is a Mac Book Pro laptop with regular backups of the MySQL database. So installing it on a serious server machine is out of the question.

The user has Java8 / Tomcat8 stack and runs the server from command line. The application worked so fine until now.

Every time the user prints a report using Jasper (code later), a tray icon will open displaying the Java Control Panel icon.

Clicking on that icon does nothing, it is just annoying. I could see that the window (from the Finder top bar) is called Bootstrap. No menu action except Exit is available. Clicking Exit kills Tomcat server.

Code follows:

@Override
public byte[] generateReportPdf(List<?> dtos, String jasperFilename, Locale locale)
{
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    try
    {
        JasperPrint jasperPrint = createJasperPrint(dtos, jasperFilename, locale);

        JRPdfExporter exporter = new JRPdfExporter();
        SimplePdfExporterConfiguration config = new SimplePdfExporterConfiguration();
        config.setEncrypted(true);
        config.set128BitKey(true);
        config.setOwnerPassword(PSW_CRYPT_PDF);
        config.setPermissions(PdfWriter.ALLOW_PRINTING);

        exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
        exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(byteArrayOutputStream));
        exporter.setConfiguration(config);
        exporter.exportReport();
    }
    catch (Exception e)
    {
        logger.error(e.getMessage(), e);
        throw new RuntimeException("PDF error", e);
    }
    finally
    {
        try
        {
            byteArrayOutputStream.close();
        }
        catch (IOException e)
        {
            logger.error(e.getMessage(), e);
        }
    }

    return byteArrayOutputStream.toByteArray();
}

private JasperPrint createJasperPrint(List<?> dtos, String jasperFilename, Locale locale) throws JRException
{
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    InputStream inputStream = classLoader.getResourceAsStream(JASPER_REPORT_FOLDER + jasperFilename + ".jrxml");
    JasperDesign jasperDesign = JRXmlLoader.load(inputStream);
    JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign);
    //JasperReport jasperReport = (JasperReport) JRLoader.loadObject(classLoader.getResource(JASPER_REPORT_FOLDER + jasperFilename + ".jasper"));

    // set mas pages and timeout
    jasperReport.setProperty(MaxPagesGovernor.PROPERTY_MAX_PAGES_ENABLED, "true");
    jasperReport.setProperty(MaxPagesGovernor.PROPERTY_MAX_PAGES, "50");
    jasperReport.setProperty(TimeoutGovernor.PROPERTY_TIMEOUT_ENABLED, "true");
    jasperReport.setProperty(TimeoutGovernor.PROPERTY_TIMEOUT, "60000");
    jasperReport.setProperty("net.sf.jasperreports.default.font.name", "DejaVu Sans");
    jasperReport.setProperty("net.sf.jasperreports.default.pdf.embedded", "true");
    jasperReport.setProperty("net.sf.jasperreports.default", "DejaVu Sans");

    JRBeanCollectionDataSource jrDataSource = new JRBeanCollectionDataSource(dtos, false);

    HashMap<String, Object> parameters = new HashMap<>();
    parameters.put(JRParameter.REPORT_LOCALE, locale);
    return JasperFillManager.fillReport(jasperReport, parameters, jrDataSource);
}
The question is

Why does it happen only on Mac? How to prevent the tray icon from appearing? We developers are running either Windows 10 or SUSE Linux hosts, and we are not having this issue on our machines, not even on a SIT environment running Windows 7.


Answer:

JasperReports uses AWT for things like text measurements and image processing. When a Java process runs in a graphical environment (i.e. as a process of a user who is logged into a desktop environment session), the AWT classes use the environment for graphical processing by default. Mac OS interprets the graphical processing as an evidence that the process has some kind of UI, and thus includes the Java process as an application in the taskbar.

If you don't want that to happen, you can force the Java process into headless AWT mode by adding -Djava.awt.headless=true to the Java options in Tomcat. That would result in AWT using a headless graphical environment implementation instead of the desktop environment.

Question:

I'm getting this error when i try to use JasperReports in a multi-thread way.

java.lang.NullPointerException
 at java.awt.color.ICC_Profile.activateDeferredProfile(ICC_Profile.java:1086) ~[?:1.7.0_80]
 at java.awt.color.ICC_Profile$1.activate(ICC_Profile.java:742) ~[?:1.7.0_80]
 at sun.java2d.cmm.ProfileDeferralMgr.activateProfiles(ProfileDeferralMgr.java:95) ~[?:1.7.0_80]
 at java.awt.color.ICC_Profile.getInstance(ICC_Profile.java:775) ~[?:1.7.0_80]
 at com.lowagie.text.Jpeg.processParameters(Unknown Source) ~[redoute-vendororderlifecycle-batch-deliverynotecrt-96.0.jar:96.0.0]
 at com.lowagie.text.Jpeg.<init>(Unknown Source) ~[redoute-vendororderlifecycle-batch-deliverynotecrt-96.0.jar:96.0.0]
 at com.lowagie.text.Image.getInstance(Unknown Source) ~[redoute-vendororderlifecycle-batch-deliverynotecrt-96.0.jar:96.0.0]
 at net.sf.jasperreports.engine.export.JRPdfExporter$InternalImageProcessor.processImageRetainShape(JRPdfExporter.java:1742)

This is a bug in Java since 1.6

Loading ICC color profiles from multiple threads sometimes triggers a null pointer exception inside the JRE's ICC_Profile class.

At this time i resolve it by calling JasperReports Export to PDF function into a Syncronized function but it's like a bottleneck in a multi-thread application.

@fabiofdsantos say on GitHub this:

"Since jasperreports is using Lowagie (deprecated since 2012), a possible workaround is to call Image.getInstance(renderer.getData(jasperReportsContext)); synchronized."

But I have no idea how to do it whit JasperReports and in the same way he use a syncronized method at the end, but maybe is a better solution than mine.


Answer:

I figure out this issue by reemplacing all JPEG image into my project that were used by jasper files. I used PNG image instead of JPEG. Even i havenĀ“t needed use a syncronized method.