3

I discovered a strange behavior when I want to use a Font, that is included in the springboot fat jar. When running tests on my local machine, which loads a Font from the /resources directory, it works perfectly. But if I build an app with maven and run it from the terminal, then I will receive:

java.io.IOException: Problem reading font data.
at java.awt.Font.createFont0(Font.java:1000)
at java.awt.Font.createFont(Font.java:877)

I tried to find a solution and did the following:

  • using different Fonts (sometimes with another error about a damaged table or sth.)
  • built various versions of docker openjdk containers to test behavior
  • build a ubuntu container, installed openjdk
  • used a debian image, installed fc-cache and made the Font available via
    • /usr/share/fonts/truetype/europlate
    • fc-cache -f -v
  • created a temp-File, just to be sure, that there is no access problem from within the jar.
  • ran the application from my terminal, where loading the font fails, too
  • using the font works on Editors, and even when the app runs from the IDE (no test)

The method:

     public Font getFont() throws IOException, FontFormatException {
        File f = File.createTempFile("dang", "tmp");
        assert f != null;
        f.delete();
        ClassLoader classLoader = getClass().getClassLoader();
        Font font = Font.createFont(Font.TRUETYPE_FONT, classLoader.getSystemResourceAsStream("EuroPlate.ttf"));
        font.deriveFont(105f);
        System.out.println(font.getFontName());
        return font;
    }

EDIT: IDE runs application -> works

Terminal runs application -> fails

Stacktrace:

java.io.IOException: Problem reading font data.
    at java.desktop/java.awt.Font.createFont0(Font.java:1183)
    at java.desktop/java.awt.Font.createFont(Font.java:1052)
    at components.NumberPlateUtility.getFont(NumberPlateUtility.java:81)
    at components.NumberPlateUtility.completeImage(NumberPlateUtility.java:173)
    at main.NumberplateClientCommands.one(NumberplateClientCommands.java:63)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:223)
    at org.springframework.shell.Shell.evaluate(Shell.java:169)
    at org.springframework.shell.Shell.run(Shell.java:134)
    at org.springframework.shell.jline.InteractiveShellApplicationRunner.run(InteractiveShellApplicationRunner.java:84)
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:783)
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:773)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1242)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1230)
    at main.Main.main(Main.java:20)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
    at org.springframework.boot.loader.Launcher.launch(Launcher.java:87)
    at org.springframework.boot.loader.Launcher.launch(Launcher.java:50)
    at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)

The repository: https://github.com/Semo/numberplate_generator.git

Semo
  • 783
  • 2
  • 17
  • 38
  • Are you using the same jdk `openjdk` in development environment? – Malathi Jul 11 '19 at 07:11
  • @Malathi Well, I tried so many, that I don't know. I will make sure right now and tell the result. RESULT: Same issue, though same versions. – Semo Jul 11 '19 at 07:19
  • 1
    What are you trying to _do_ with the font? It's extremely uncommon for a Boot application to manipulate a font (as opposed to, for example, serving it to clients to use in the browser). – chrylis -cautiouslyoptimistic- Jul 11 '19 at 08:12
  • You meant that you use openjdk x without docker and use the same openjdk x version with docker build. And you get the issue in the dockerised container and not in the environment without docker. Is that right? – Malathi Jul 11 '19 at 08:13
  • @chrylis using the font in the backend to render a String into an image and send it to a webservice for further use. – Semo Jul 11 '19 at 09:43
  • @Malathi right, excepting that I NOW found out, that the issue occurs in both places containerized/locally, when running the app straight from the terminal without any help from an IDE via `java -jar app.jar`. I pointed that out in my adapted question. – Semo Jul 11 '19 at 09:48
  • Can you extract the jar and see if it has the font? – Malathi Jul 11 '19 at 09:54
  • @Malathi Yes. It is in the root of BOOT-INF/classes. – Semo Jul 11 '19 at 10:04

1 Answers1

3

This code will work:

public Font getFont() throws IOException, FontFormatException {
        File f = File.createTempFile("dang", "tmp");
        assert f != null;
        f.delete();
        ClassLoader classLoader = getClass().getClassLoader();
        Font font = Font.createFont(Font.TRUETYPE_FONT, classLoader.getResourceAsStream("EuroPlate.ttf"));
        font.deriveFont(105f);
        System.out.println(font.getFontName());
        return font;
    }

Note the change in classLoader.getResourceAsStream. Check this answer for more explanation.

Malathi
  • 2,119
  • 15
  • 40