Full Stack Me.

Remarks on developing a versatile living.

Bring your favourite fonts to reports running in Docker

12 April 2018

Producing the perfect report is hardly a straightforward endeavour. Be it designing the most suitable layout for the content in hand, choosing the right fonts, or styling (padding, spacing, etc.), all require thought, consideration and some trialling and error. You can do all of this using your favourite document editor, or turn to professional design software for more complex layouts. However, this manual approach is more suited to ad-hoc reporting needs. For those looking to automate the generation of their reports, the tasks you’d expect to do manually will most likely be done through code using a report generation library. This usually entails creating templates (e.g. in XML), styled to meet output expectations (with your fonts, logo, etc), then passed to a reporting service along with the data for generation.

A layered problem

When such services run out of a container (e.g. Docker), something as simple as getting your chosen font to work requires a bit of effort. Why? Well, that’s because fonts are managed at an OS level (e.g. MacOS, Linux, or Windows). Since most document editors run as native applications, they can seamlessly access the fonts register and make use of any installed font. This makes things very convenient for standard users but slightly trickier for developers looking to capitalise on containerisation.

As an example, let consider Jaspersoft Studio, an editor for one of the most popular enterprise-grade reporting engines, JasperReports. It enables you to use any font installed in the OS when designing templates. You could use JasperReports Java library to create a service that will process your template (which you created using Jaspersoft Studio), along with the data, and it will produce a beautiful report on your local Windows machine. Now run this very same service via a Docker container on a Linux server, and your report may look off.

But why does this happen? This occurs because the whole service structure is now multi-layered, going top-to-bottom. It roughly looks as follows:

Multilayered services structure when accessing fonts

The top layer is our Reports Library (e.g. JasperReports, Birt). This layer doesn’t include any fonts and can only retrieve them using the Java platform package (java.awt.*).

Then we have JRE, which supports loading physical and logical fonts from the underlying platform. This, however, only bundles several Lucida fonts (more info). Obviously, we do not want to limit ourselves to a single family of fonts, so let’s dig deeper.

The Docker Engine layer hosts the containers where our services are run. Containers often come with some Linux distro, e.g. Alpine or CentOS, and may have some fonts installed by default. In many cases though, there will be no fonts available, as containers are designed to be very ascetic and lightweight, and hence exclude things like fonts.

The last layer is the Server OS (e.g. CentOS, RedHat). As a complete, fully packed OS, it comes bundled with some fonts, mostly DejaVu, Classical Serif and SansSerif. Having access to the OS enables us to install any custom fonts we want. To make them available for JasperReports, we will have to pass it through all the layers described above.

Cutting through the layers

Here is the step-by-step guide on how to enable custom fonts (PT Sans in this case) for JasperReports, or any other reports library.

Before we start, ensure the following packages are installed:

wget, unzip, fc-list (fontconfig or /usr/bin/fc-list)

Then, we create a local folder ~/.fonts. We will use it to store temporary fonts files.

Download a ZIP with custom fonts using wget and unzip it, as follows:

$ wget https://www.paratype.ru/uni/public/PTSans.zip \
 -O ~/.fonts/PTSans.zip
$ cd ~/.fonts 
$ unzip PTSans.zip

Now, to make our custom fonts system-wide, we need to copy them to the /usr/share/fonts/ :

$ sudo cp -rfv .fonts /usr/share/fonts/
$ cd /usr/share/fonts/
$ sudo mv .fonts/ pt_sans/

Once copied, reset the fonts cache:

$ cd pt_sans
$ fc-cache -fv

This is a good point to check if the fonts were applied correctly and can be used in Java. To do this, we create a very simple Java application consisting of one class:

import java.awt.GraphicsEnvironment;

public class ListFonts {
    public static void main(String args[]) {
        GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment();
        for(String font:e.getAvailableFontFamilyNames()) {

Run the code to display available fonts. An example output would look like this:

$ javac ListFonts.java
$ java ListFonts
Bitstream Charter
Century Schoolbook L
Courier 10 Pitch
DejaVu Serif
DejaVu Serif Condensed
Nimbus Mono L
Nimbus Roman No9 L
Nimbus Sans L
PT Sans
PT Sans Caption
PT Sans Narrow
Standard Symbols L
URW Bookman L
URW Chancery L
URW Gothic L
URW Palladio L

Packing fonts to the container

As you can see, our PT Sans fonts were successfully installed and are available to the Java application. This means, we can now make our custom fonts available to our JasperReports running in a Docker container. One way to do this is to attach the fonts folder as a volume to the container, with -v <fonts folder> command. When added to docker run, it may look like following:

$ docker run -d --name reports \
  -v /usr/share/fonts/pt_sans:/usr/share/fonts/pt_sans \

When the container starts, check the fonts available:

$ docker exec -it reports sh
$ fc-list

You should see a similar list of fonts as it was returned by our sample Java application. That means, JRE running in reports container now has an access to all custom fonts. In turn, JasperReports can ask java.awt.* for these fonts and use it in reports generation. Here's how it might look in JRXML styles configuration:

<style name="column data" fontName="PT Sans" fontSize="10">
     <paragraph leftIndent="5" spacingBefore="3"/>

Hope you found this short guide useful and now ready to produce awesome reports with shiny fonts!


Back to other articles
©2021, Full Stack Me Project