I think it’s FOP that causes this. It sometimes runs out of memory while enumerating system fonts, presumably because there are quite a lot of them.
There doesn’t appear to be any way to tell it to only use specific fonts without hard-coding the path. Putting
<auto-detect/> in the FOP config file seems to be the only way to get it to look in the system font directories (see https://xmlgraphics.apache.org/fop/2.1/fonts.html).
librsvg— bleh. It also makes things less self-contained, although it isn’t really with the fonts already anyway. (See https://github.com/mkowsiak/jnicookbook.)
In the meantime, I guess watch out for the
java_pidXXXXX.hprof files and delete them when they appear.
For Iosevka, use the unhinted SS03 “fixed” version, as that doesn’t include ligatures by default and is much smaller overall (e.g., https://github.com/be5invis/Iosevka/releases/download/v17.1.0/ttf-unhinted-iosevka-fixed-ss03-17.1.0.zip).
Argh, FOP is such a POS when it comes to custom fonts.
<auto-detect /> works, but occasionally the JVM runs out of memory because of the sheer number of system fonts, where “font” has it's proper meaning of “a specific variant of a type face” (e.g., Open Sans is at least 12 different fonts). It auto-detects something like 3,500 individual fonts on my system.
Specifying embedded fonts inside a
<renderer> element as specified in the documentation doesn’t work. The fonts aren’t even registered and I’m not entirely convinced the config is properly parsed. After much experimentation and debugging and poking around in the XSD for the config file, specifying the fonts in a top-level
<fonts> element works.
The default for
<base> is not where the FOP config file is, rather it’s the “execution” base directory, usually either the root of the app or jar (but see below for Gradle). You therefore need to specify the full relative path to the fonts directory when loading a font file. This also works with a
<directory> element, which simplifies things, but it does yell a lot about something that I can't quite determine:
INFO: ignoring out of order or duplicate glyph index: 168 WARNING: coverage set class table not yet supported
Specifying the fonts individually doesn't cause this (?!).
<font-base> seem to be ignored (at least for the top-level
<fonts> element), e.g., even when
<font-base> is the full relative path:
java.io.FileNotFoundException: /Users/nstanger/tmp/pdftest/app/OpenSans-Regular.ttf (No such file or directory)
Unfortunately the whole thing breaks completely under a Gradle daemon, as the base path is set to the gradle daemon home
~/.gradle/daemon/<version> rather than the jar root. Running without a daemon sets the base path to the directory containing the current
build.gradle, so you can at least set the relative path to point to
../buildSrc, but you have to run Gradle without a daemon, which seems surprisingly more difficult than it should be. This works (https://github.com/gradle/gradle/issues/11517#issuecomment-602632415), but is a pain to use:
GRADLE_OPTS="-XX:MaxMetaspaceSize=256m -XX:+HeapDumpOnOutOfMemoryError -Xmx512m" gradle --no-daemon <target>
Someone also came up with a genius trick of munging the config on the fly (https://stackoverflow.com/a/72807651), but that still doesn’t work under Gradle, because the
getResource() call returns a URI that points inside the jar and thus isn’t a real path 🤬.
The real problem is that the FOP config file has no concept of the current execution environment, so if something like Gradle munges that, everything breaks in exciting ways.
At this stage I can see two alternatives:
For reference, the following works for XeTeX with
fontspec, assuming that the TTF files have been downloaded to
../buildSrc/build/resources/main/fonts/(relative to the lecture directory):
(If the TTF files are in
buildSrcwill copy them into the
This is using the fonts as downloaded directly from their respective repositories.
One issue is that not all of the fonts are required (this is particularly true of Iosevka, which has something like 54 different weights, styles, etc.). A solution might be to create a repository that includes just the files required and download from that.