Search code examples
javaubuntupdffontsapache-fop

Apache Batik 1.7 + Apache FOP 1.1 not rendering fonts on Ubuntu


I'm having trouble configuring fonts in apache batik + apache fop svg pdf transcoder.

The config file does not seem to be loaded on properly but only on Ubuntu. I've tried running the exact same code on MacOSX and it works just fine...

Trying to load the same svg on both systems yield different results.

The relevant bit of java is this:

try {
   DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder();
   Configuration effCfg = cfgBuilder.buildFromFile(new File(this.fontCfgFilePath));
   if (effCfg != null) {
        PDFDocumentGraphics2DConfigurator configurator = new PDFDocumentGraphics2DConfigurator();
        configurator.configure(graphics, effCfg, false);
   } else {
        graphics.setupDefaultFontInfo();
   }
} catch (Exception e) {
   throw new TranscoderException("Error while setting up PDFDocumentGraphics2D", e);
}

but the full version is only a slightly modified version of

https://github.com/naofum/thinreports-java/blob/master/src/com/github/naofum/thinreports/MultiPagePDFTranscoder.java

The only difference is how the config file is passed. in the original the config file is hardcoded in my version is passed via in via the constructor.

this.fontCfgFilePath is set in the constructor with the value "resources/fonts.cfg.xml"

the folder structure is

project/
   resources/
      font.cfg.xml
      fonts/
         0b0b385a-f6e8-4a33-887f-2f178a576139.ttf
         ... a bunch of other fonts ....
   pdf-generator.jar
   ... other files that are not relevant to the problem...

The font.cfg.xml is setup like this

<configuration>
  <fonts>
    ... other fonts ....
    <font kerning="no" embed-url="file:resources/fonts/0b0b385a-f6e8-4a33-887f-2f178a576139.ttf">
        <font-triplet name="Papyrus" style="normal" weight="normal"></font-triplet>
    </font>
    <font kerning="no" embed-url="file:resources/fonts/0b0b385a-f6e8-4a33-887f-2f178a576139.ttf">
        <font-triplet name="Papyrus" style="normal" weight="bold"></font-triplet>
    </font>
    <font kerning="no" embed-url="file:resources/fonts/0b0b385a-f6e8-4a33-887f-2f178a576139.ttf">
        <font-triplet name="Papyrus" style="italic" weight="normal"></font-triplet>
    </font>
    <font kerning="no" embed-url="file:resources/fonts/0b0b385a-f6e8-4a33-887f-2f178a576139.ttf">
        <font-triplet name="Papyrus" style="italic" weight="bold"></font-triplet>
    </font>
    ... other fonts
  </fonts>
</configuration>

The exact same project with the exact same configs if run both on Ubuntu and MacOSX.

MacOSX generates the correct PDF with the correct fonts

Ubuntu does not....

The interesting bit is that Ubuntu does not even try to use the fonts...

If i remove a font from the config file on mac it will not render the font (as expected)

If i change the font file to something invalid, again on mac, it throws FileNotFoundException.

But on Ubuntu none of this matters... Basically it looks like the font file is never read.

I've tried adding the fonts directly in system (at /usr/share/fonts) and force updating the font cache on ubuntu with "fc-cache -fv" but that did not help.

I've also tried various embed-url links (in the config) but it did not have any effect (i.e. changing embed-url="file://resources..." to embed-url="file:///resources..." and other variations).

Both systems run java1.8 (ubuntu in on Oracle JDK 1.8.151 and mac is Oracle JDK 1.8.131)


Solution

  • After much searching online and reading source code both in batik/fop and java its self i've found that Batik and FOP need to have fonts present in the java.awt.GraphicsEnvironment

    java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment() 
    

    will get the GraphicsEnvironment instance on which you can call

    ge.getAvailableFontFamilyNames()
    

    to get an array of currently loaded font families.

    Note that .ttf fonts don't need to have font families defined in order to be correctly loaded.

    But Batik and FOP DO need to the correct font family to be specified...

    As it happens MacOSX knows somehow (upon installing a system level font) to set the correct font family. But GNU/Linux seams to not do the same thing.

    If a font is not available system wide (which was the case for me on linux) it has to be registered in java.

    That can be done with the GraphicsEnvironment like so:

    boolean loaded = ge.registerFont(java.awt.Font.createFont(java.awt.Font/TRUETYPE_FONT, new File("<path to font>"));
    

    if loaded is true then the font was correctly loaded and it will register with batik/fop and render correctly. Note that the font.cfg.xml (from my question) still needs to be present and the path of the font needs to match the regitered font.

    if loaded is false then the font was not loaded.

    The main reason for this seems to be that a font with the same font family OR the same font name is already registered. (see here http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/sun/font/FontManager.java#2921)

    It's also important that the fonts are registered before any rendering happens since batik/fop caches the fonts somewhere and does not refresh them.

    The solution in my case was to remove the system font and keep the local one.

    So my problem was

    1. none of my .ttf fonts had a proper font family defined

    2. the font's were not registered in java correctly.

    My solution was

    1. For 1. Use fontforge to add the correct font family to all my fonts.
    2. For 2. Register all my fonts with the GraphicsEnvironment as decribed above.
    3. Extra Step. Remove all old fonts (the ones without the correct font family) from my ubuntu system because i had added them to see if it fixed anything.