Jasper Reports using unavailable fonts

Jasper Reports using unavailable fonts

Problem Statement

Many a times we design Jasper Report (JRXML) on one machine and deploy code on a server on a different machine. It may so happen that we have a huge number of fonts on designer machine (e.g. Mac machine) and we do not have many fonts on server (say a Unix server).
So generating output using JRXML may pass on design machine, but fails on server because it is not able to find our font on server.

This article discusses a few ways we can solve this issue.

Prerequisite

I will be continuing with my example in blog "Using Jasper Reports to generate dynamic Images".

The only changes I have made compared with JRXML in that blog are:
  • Name - Comic Sans MS
  • Designation - Courier New 
And source code looks like

<?xml version="1.0" encoding="UTF-8"?>
<!-- Created with Jaspersoft Studio version 6.3.1.final using JasperReports Library version 6.3.1  -->
<!-- 2017-02-10T00:28:26 -->
<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="visiting_card" pageWidth="300" pageHeight="200" columnWidth="260" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="8acaa693-649a-48da-845d-992da9f3b4c6">
    <detail>
        <band height="160" splitType="Stretch">
            <staticText>
                <reportElement x="0" y="90" width="100" height="30" uuid="89272bdc-07f8-485c-bcc4-5551232296c7"/>
                <textElement>
                    <font size="16"/>
                </textElement>
                <text><![CDATA[Designation:]]></text>
            </staticText>
            <textField>
                <reportElement x="0" y="31" width="260" height="29" uuid="37cdb745-6f42-4a33-9d3c-8e712df95ef1"/>
                <textElement>
                    <font fontName="Comic Sans MS" size="16"/>
                </textElement>
                <textFieldExpression><![CDATA[$P{REPORT_PARAMETERS_MAP}.get( "name" )]]></textFieldExpression>
            </textField>
            <textField>
                <reportElement x="100" y="90" width="160" height="20" uuid="5dff3cdf-2e28-4175-9160-a6ea009cfe69"/>
                <textElement>
                    <font fontName="Courier New" size="16"/>
                </textElement>
                <textFieldExpression><![CDATA[$P{REPORT_PARAMETERS_MAP}.get( "designation" )]]></textFieldExpression>
            </textField>
        </band>
    </detail>
</jasperReport>

Java code to convert this JRXML into image is also same as in my blog "Using Jasper Reports to generate dynamic Images". Only change is that I changed designation from "Principal Engineer" to "Principal Engg.".

Output

Assuming that we have both fonts available on our machine, everything runs smoothly and we have a lovely output image. It looks like:

Issue

Assuming that font Comic Sans MS is unavailable on my machine. 
I get a beautiful exception

Exception in thread "main" net.sf.jasperreports.engine.util.JRFontNotFoundException: Font "Comic Sans MS" is not available to the JVM. See the Javadoc for more details.
    at net.sf.jasperreports.engine.fonts.FontUtil.checkAwtFont(FontUtil.java:652)
    at net.sf.jasperreports.engine.fill.SimpleTextLineWrapper.loadFont(SimpleTextLineWrapper.java:369)
    at net.sf.jasperreports.engine.fill.SimpleTextLineWrapper.getGeneralFontInfo(SimpleTextLineWrapper.java:339)
    at net.sf.jasperreports.engine.fill.SimpleTextLineWrapper.createFontInfo(SimpleTextLineWrapper.java:279)
   ...
Exception clearly states that JVM was unable to load font. Now what.
We can
  • EITHER ignore this error and let Jasper fallback to some font it has
  • OR bundle our new font along with source code so that it runs perfectly on any machine
Lets explore both of these approaches.

Solution 1: Ignore unavailable font (using JRXML)

Just below tag, add following line:
<property name="net.sf.jasperreports.awt.ignore.missing.font" value="true"/>

Jasper falls back to a font it has. But if we would want it to fallback to specific font, we also add following line to JRXML
<property name="net.sf.jasperreports.default.font.name" value="Sans Serif"/>

Now we wouldn't get an error. And my image generated looks like (without Sans Serif fallback)
Notice difference in font for "name".

Along with this I also got warnings saying "Comic Sans MS" wasn't found. This could be useful while searching logs for fonts we might need to install.
Feb 10, 2017 12:44:12 AM net.sf.jasperreports.engine.fonts.FontUtil checkAwtFont
WARNING: Font 'Comic Sans MS' is not available to the JVM. For more details, see http://jasperreports.sourceforge.net/api/net/sf/jasperreports/engine/util/JRFontNotFoundException.html
Feb 10, 2017 12:44:13 AM net.sf.jasperreports.engine.fonts.FontUtil checkAwtFont
WARNING: Font 'Comic Sans MS' is not available to the JVM. For more details, see http://jasperreports.sourceforge.net/api/net/sf/jasperreports/engine/util/JRFontNotFoundException.html

Solution 2: Ignore unavailable font (using properties file)

One good thing about Jasper is that it allows us to configure itself. It looks for "jasperreports.properties" file in classpath.

So create this file in classpath and have following entry
net.sf.jasperreports.awt.ignore.missing.font=true

This has same effect.

Solution 3: Export fonts from Jasper Studio

Jasper Studio has ability to bundle a font so that it can be shipped along with java code. This bundle contains font file along with some descriptors and can be coded by hand. It is not necessary to create this bundle using Jasper Studio. But for sake of simplicity, let me explain using Jasper Studio.

Step 1: Open Jasper Studio 
Step 2: Go to preferences
Step 3: Search for "font" in filter
Step 4: Click on "Fonts". This will open page from where fonts can be exported.
My preferences look as below. Notice that I searched for "font" in search box to narrow down to "Fonts" on left pane.

Step 5: Click on Add.
Step 6: It now asks for a "Family Name". We can enter anything. Let me type "ComicSans".
Step 7: Click "Browse" button against "TrueType" because Comic Sans is a True Type font (*.ttf). Navigate to location where this font is stored.
My screen looks like

 Notice that there are tabs for bold, italic and bold italic because some fonts have these versions too. So make sure you select fonts for these too.

Step 8: Click "Finish". Now we get to see our newly added font in our pane.
Step 9: Select this newly created font, and click on "Export" button. Export this font as a zip.
I want to export as a zip because I will be extracting its contents to place on my classpath.

Now comes tricky part. Let us extract these contents and place them as is on classpath. (note - not the parent folder. The contents i.e. jasperreports_extension.properties and fonts folder)

Examining exported files:
jasperreports_extension.properties
Contains information about fonts that we would want Jasper to load when generating images for us. My file has a line that reads

net.sf.jasperreports.extension.simple.font.families.ireportfamily1486668951557=
     fonts/fontsfamily1486668951557.xml

Notice that number in "ireportfamily1486668951557" (in key) is same as number in "fontsfamily1486668951557" (in value).

"fonts" folder
Contains fonts and meta data.

fonts/family1486668951557.xml
This is inside fonts folder. It is metadata about font.
It tells location of normal and bold fonts for me.
It looks like:
<?xml version="1.0" encoding="UTF-8"?>
<fontFamilies>
    <fontFamily name="ComicSans">
        <normal><![CDATA[fonts/ComicSans/Comic Sans MS.ttf]]></normal>
        <bold><![CDATA[fonts/ComicSans/Comic Sans MS Bold.ttf]]></bold>
        <exportFonts/>
    </fontFamily>
</fontFamilies>

fonts/ComicSans/*
These are the actual font files.
Notice folder name - ComicSans - this is the name I gave when adding "font" in Font Preferences pane in Jasper Studio.

With this, we should be good to go and generate image with our required font.


Exporting more fonts

One way to export multiple fonts together is to create them together and then export. But it may so happen that we add fonts incrementally.
So just export new font and extract ZIP file's contents.

Open "jasperreports_extension.properties" and copy line "net.sf.jasperreports.extension.simple.font.families" and paste it into  already existing "asperreports_extension.properties" in your classpath. We just need to tell Jasper about new font's metadata (xml file with number).

And then just copy-paste contents of "fonts" folder into your existing "fonts" folder.

Summary

I prefer combining Solution 2 and Solution 3 together.


InvalidFontException and FontFormatException

Sometimes when we include some type of TTF fonts, we get these exceptions:
e.g.
net.sf.jasperreports.engine.fonts.InvalidFontException: Error loading font "fonts/Herculanum/Herculanum.ttf"

Caused by: java.awt.FontFormatException: Font name not found

Root cause for these lie in JVM's implementation.
Each font has a property "platformID" to identify that font's platform. For Mac fonts, this id is 1. For Windows fonts, this id is 3.
Now Java class "sun.font.TrueTypeFont" when loading fonts, loads fonts for which "platformID" is 3. And if platformID" is 1, it ignores and thereby causing issue.

For details, Refer: http://stackoverflow.com/questions/21268694/loading-some-truetype-font-from-ttf-file-in-java-causes-fontformatexception-fon

A few sites talk about editing a font and assigning proper names to fix this issue (see exception reads "Font name not found") So they add font name. But I haven't tried it because I couldn't get my hands on a font editor.











Comments