Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I configure simple Java fontconfig.properties file for use on Linux

I am using a custom Java 11 runtime on custom linux hardware, the Java runtime was not built my me. But I have a problem my application requires access to a font and the runtime is not configured with any so I get this stacktrace

Exception in thread "main" java.lang.InternalError: java.lang.reflect.InvocationTargetException
        at java.desktop/sun.font.FontManagerFactory$1.run(FontManagerFactory.java:86)
        at java.base/java.security.AccessController.doPrivileged(Native Method)
        at java.desktop/sun.font.FontManagerFactory.getInstance(FontManagerFactory.java:74)
        at java.desktop/java.awt.Font.getFont2D(Font.java:497)
        at java.desktop/java.awt.Font.getFamily(Font.java:1410)
        at java.desktop/java.awt.Font.getFamily_NoClientCode(Font.java:1384)
        at java.desktop/java.awt.Font.getFamily(Font.java:1376)
        at java.desktop/java.awt.Font.toString(Font.java:1869)
        at java.base/java.lang.String.valueOf(String.java:2951)
        at java.base/java.io.PrintStream.println(PrintStream.java:897)
        at Fonts.main(Fonts.java:7)
Caused by: java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
        at java.desktop/sun.font.FontManagerFactory$1.run(FontManagerFactory.java:84)
        ... 10 more
Caused by: java.lang.NullPointerException
        at java.desktop/sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1262)
        at java.desktop/sun.awt.FontConfiguration.readFontConfigFile(FontConfiguration.java:225)
        at java.desktop/sun.awt.FontConfiguration.init(FontConfiguration.java:107)
        at java.desktop/sun.awt.X11FontManager.createFontConfiguration(X11FontManager.java:719)
        at java.desktop/sun.font.SunFontManager$2.run(SunFontManager.java:367)
        at java.base/java.security.AccessController.doPrivileged(Native Method)
        at java.desktop/sun.font.SunFontManager.<init>(SunFontManager.java:312)
        at java.desktop/sun.awt.FcFontManager.<init>(FcFontManager.java:35)
        at java.desktop/sun.awt.X11FontManager.<init>(X11FontManager.java:56) 

I can provide some fonts and I have worked out I need to create a fontconfig.properties and put i into the Java runtimes lib folder, but I am struggling to understand what I need to put into fontconfig.properties.

Can someone give me an example of how to specify a minimal set of fonts in fontconfig.properties on linux to prevent the exception occurring.

More specificially, I have a set of truetype fonts that I have put into a fonts folder within the lib folder so how do I use this set as a set of fonts available to Java

  • LucidaBrightDemiItalic.ttf
  • LucidaBrightRegular.ttf
  • LucidaSansRegular.ttf
  • LucidaTypewriterRegular.ttf
  • LucidaBrightDemiBold.ttf
  • LucidaBrightItalic.ttf
  • LucidaSansDemiBold.ttf
  • LucidaTypewriterBold.ttf

If I create an empty fontconfig.properties file then the first exception changes to

Caused by: java.lang.NullPointerException
        at java.desktop/sun.awt.FontConfiguration.getInitELC(FontConfiguration.java:465)
        at java.desktop/sun.awt.FontConfiguration.initFontConfig(FontConfiguration.java:441)
        at java.desktop/sun.awt.FontConfiguration.init(FontConfiguration.java:108)
        at java.desktop/sun.awt.X11FontManager.createFontConfiguration(X11FontManager.java:719)
        at java.desktop/sun.font.SunFontManager$2.run(SunFontManager.java:367)
        at java.base/java.security.AccessController.doPrivileged(Native Method)
        at java.desktop/sun.font.SunFontManager.<init>(SunFontManager.java:312)
        at java.desktop/sun.awt.FcFontManager.<init>(FcFontManager.java:35)
        at java.desktop/sun.awt.X11FontManager.<init>(X11FontManager.java:56)

so this shows Java runtime is at least finding the (empty) fontconfig.properties file so if I can configure it correctly this should work.

I tried to create a very minimal fontconfig.properties file with one file but it didnt work.

version=1

allfonts.plain.latin-1=-monotype-times new roman-medium-r-normal--*-%d-*-*-p-*-iso10646-1

filename.-monotype-times new roman-medium-r-normal--*-%d-*-*-p-*-iso10646-1=/mnt/app/opt/SongKong/songkong/jre/lib/fonts/ipag.ttf

awtfontpath.latin-1=/mnt/app/opt/SongKong/songkong/jre/lib/fonts
like image 948
Paul Taylor Avatar asked Apr 04 '20 07:04

Paul Taylor


People also ask

What is Fontconfig in Ubuntu?

Fontconfig is a library designed to provide system-wide font configuration, customization and application access.

What fonts are in Java?

Logical fonts are the five font families defined by the Java platform which must be supported by any Java runtime environment: Serif, SansSerif, Monospaced, Dialog, and DialogInput. These logical fonts are not actual font libraries.

Do we need a basic configuration directive for fonts in Java?

Sure, all that configuration flexibility probably has its purpose. But come on man, you also need a basic configuration directive with suitable defaults so you don't have to become an expert in the entire Java font ecosystem to add a single font file.

Can users create their own font configuration files?

Users can edit or create their own font configuration files to adjust the mappings to their particular system setup. Note however that this is a modification of the JRE, and Sun does not support modified JREs. Font configuration files come in two formats: A properties format and a binary format.

What is the default path name for fonts in Java?

On Solaris and Linux, absolute path names, path names starting with "$JRE_LIB_FONTS" for the runtime environment's own lib/fonts directory, or xlfd names are used. The Java runtime can automatically determine a number of directories that contain font files, such as its own lib/fonts directory or the Windows fonts folder.

How do I substitute a font in fontconfig?

Substituting Fonts When an application requests a font that is not available on the system, fontconfig reads the /etc/fonts/fonts.conf configuration file to determine the most similar available font to substitute for the requested font. Individual characters can also be substituted if they are not present in the requested font.


1 Answers

Jdk releases do not come with any fonts, Oracle releases generally do, so I had an idea to download an Oracle release and see what they do but the fontconfig.properties file differs significantly between Windows and UNIX releases so I needed a UNIX release.

I first downloaded jdk-11.0.6_linux-x64_bin.tar.gz but this had no fontconfig.properties file probably because it was just a generic linux buiild rather than tied to any in particular. Since my main dev machine is Windows I wasn't keen to try out the .deb or .rpm builds because no easy route for me to install them. So instead I download the Solaris jdk-11.0.6_solaris-sparcv9_bin.tar.gz and unzipped that.

That did contain a font.properties.src file following this structure

Version =1
# Component Font Mappings
# Search Sequences
# Font Filenames
# AWT X11 font paths

My understanding is the Component Font Mappings map from a Java component font to a logical font name, Search Sequence specifies an order to search for fonts based on the Java component fonts. Font Filenames map from logical font name to actual filename where the font is located on machine. AWT X11 font paths specifies from the component font name to the actual folder containing the actual font on machine.

So I did a search and replace on the file replacing actual filename with the location on my font on the server, and replacing actual folder with the location on the folder containing the actual font.

I then renamed this modified fontconfig.proprties.src to fontconfig.properties and stored it in the jre/lib folder

A simple test program that previously failed now works

import java.awt.*;
public class Fonts
{
     public static void main(String[] args) throws Exception
     {
         Font defaultFont = Font.decode(null);
         System.out.println(defaultFont);
     }
}

However I have only specified one font (ipag.ttf) to be used for different scripts and for different styles (plain, bold etc).

When I run the program the fonts are required for use with jakarta.poi (for creating an excel spreadsheet file) and this now give the following exception:

java.lang.ClassCastException: class sun.font.CompositeFont cannot be cast to class sun.font.PhysicalFont (sun.font.CompositeFont and sun.font.PhysicalFont are in module java.desktop of loader 'bootstrap')
    at java.desktop/sun.font.SunFontManager.getDefaultPhysicalFont(SunFontManager.java:1086)
    at java.desktop/sun.font.SunFontManager.initialiseDeferredFont(SunFontManager.java:965)
    at java.desktop/sun.font.SunFontManager.findOtherDeferredFont(SunFontManager.java:903)
    at java.desktop/sun.font.SunFontManager.findDeferredFont(SunFontManager.java:919)
    at java.desktop/sun.font.SunFontManager.findFont2D(SunFontManager.java:2120)
    at java.desktop/java.awt.Font.getFont2D(Font.java:506)
    at java.desktop/java.awt.Font.canDisplayUpTo(Font.java:2246)
    at java.desktop/java.awt.font.TextLayout.singleFont(TextLayout.java:469)
    at java.desktop/java.awt.font.TextLayout.<init>(TextLayout.java:530)
    at org.apache.poi.ss.util.SheetUtil.getDefaultCharWidth(SheetUtil.java:275)

I think the problem here is that a Physical font was expected but because I had not specified bold, italic fonts ectera Java try to create a font based on modifications to a physical font, creating Composite font. But Java always expects Physical fonts to be provided for some basic styles.

So I then copied the Lucida fonts to the fonts dir, modified the fontconfig. properties file to use these these font variations as follows

filename.-monotype-arial-medium-r-normal--*-%d-*-*-p-*-iso10646-1=/mnt/app/opt/jre/lib/fonts/LucidaSansRegular.ttf
filename.-monotype-arial-medium-i-normal--*-%d-*-*-p-*-iso10646-1=/mnt/app/opt/jre/lib/fonts/LucidaSansRegular.ttf
filename.-monotype-arial-bold-r-normal--*-%d-*-*-p-*-iso10646-1=/mnt/app/opt/jre/lib/fonts/LucidaSansDemiBold.ttf
filename.-monotype-arial-bold-i-normal--*-%d-*-*-p-*-iso10646-1=/mnt/app/opt/jre/lib/fonts/LucidaSansDemiBold.ttf
filename.-monotype-courier_new-medium-r-normal--*-%d-*-*-p-*-iso10646-1=/mnt/app/opt/jre/lib/fonts/LucidaTypewriterRegular.ttf
filename.-monotype-courier_new-medium-i-normal--*-%d-*-*-p-*-iso10646-1=/mnt/app/opt/jre/lib/fonts/LucidaTypewriterRegular.ttf
filename.-monotype-courier_new-bold-r-normal--*-%d-*-*-p-*-iso10646-1=/mnt/app/opt/jre/lib/fonts/LucidaTypewriterBold.ttf
filename.-monotype-courier_new-bold-i-normal--*-%d-*-*-p-*-iso10646-1=/mnt/app/opt/jre/lib/fonts/LucidaTypewriterBold.ttf
filename.-monotype-times_new_roman-medium-r-normal--*-%d-*-*-p-*-iso10646-1=/mnt/app/opt/jre/lib/fonts/LucidaBrightRegular.ttf
filename.-monotype-times_new_roman-medium-i-normal--*-%d-*-*-p-*-iso10646-1=/mnt/app/opt/jre/lib/fonts/LucidaBrightItalic.ttf
filename.-monotype-times_new_roman-bold-r-normal--*-%d-*-*-p-*-iso10646-1=/mnt/app/opt/jre/lib/fonts/LucidaBrightDemiBold.ttf
filename.-monotype-times_new_roman-bold-i-normal--*-%d-*-*-p-*-iso10646-1=/mnt/app/opt/jre/lib/fonts/LucidaBrightDemiItalic.ttf

Just kept the ipag.ttf for South East Asian scripts and restarted application and that worked. Whether or not this will work in all circumstances I am not sure.

like image 137
Paul Taylor Avatar answered Oct 24 '22 23:10

Paul Taylor