Linux – way to make PhantomJS (or any headless browser) use an alternate font cache

command linefontslinux

Is there a way to make PhantomJS (or any headless browser) use an alternate font cache besides /usr/share/fonts/? One way to use more fonts (e.g. CJK fonts) with PhantomJS is to install them to this directory. However, this is a shared server and cannot be done.

I cannot seem to find a CLI parameter for this. Please forgive me if this is a silly question.

This is a RedHat build, and yum and rpm are disabled.

Screenshot with PhantomJS – fonts not loading:

Failed font rendering

Desired result (http://v1.jontangerine.com/silo/typography/web-fonts/):

Actual fonts rendering


SOLVED: @grochmal showed me that fonts can be installed in the home folder. I ran fc-cache -vf and the system fonts and the ~/.fonts/TTF fonts get cached. For example, running fc-list "impact" finds the Impact font (for personal use only):

> fc-list impact
Impact:style=Regular,Normal,obyčejné,Standard,Κανονικά,Normaali,Normál,Normale,Standaard,Normalny,Обычный,Normálne,Navadno,Arrunta

I confirmed this with the stack trace cleverly suggested by @grochmal:

strace ./phantomjs ../examples/rasterize.js http://example.com img.jpg 2>&1 | grep font

and found out that PhantomJS is indeed looking in my user fonts directory

open("/home/user1/.fonts/TTF/verdana.ttf", O_RDONLY) = 11
open("/home/user1/.fonts/TTF/AndaleMo.TTF", O_RDONLY) = 11
open("/home/user1/.fonts/TTF/arial.ttf", O_RDONLY) = 11
open("/home/user1/.fonts/TTF/cour.ttf", O_RDONLY) = 11
open("/home/user1/.fonts/TTF/georgia.ttf", O_RDONLY) = 11
open("/home/user1/.fonts/TTF/impact.ttf", O_RDONLY) = 11
...

Best Answer

PhantomJS respects fontconfig directories and even the old font.dir/font.scale postscript font configuration. For example I have and old Type 1 font:

$ find ~/.fonts/Type1/
/home/grochmal/.fonts/Type1/
/home/grochmal/.fonts/Type1/augie___.pfb
/home/grochmal/.fonts/Type1/fonts.scale
/home/grochmal/.fonts/Type1/fonts.dir

(That was created with the ol' X11 mkfontdir)

And, for a better example, I'll copy a fotnconfig font into my home directory:

$ mkdir -p ~/.local/share/fonts/TTF
$ cp /usr/share/fonts/TTF/HomemadeApple.ttf ~/.local/share/fonts/TTF
$ fc-cache  # just in case

Now let's see how PhantomJS uses them (using a classic example from the PhantomJS github):

$ wget https://raw.githubusercontent.com/ariya/phantomjs/master/examples/rasterize.js

strace prints all system calls (including filesystem access):

$ strace phantomjs rasterize.js 2>&1 | grep font | grep grochmal |grep -v cache
stat("/home/grochmal/.config/fontconfig/conf.d", 0x7ffff95fbbc0) = -1 ENOENT (No such file or directory)
stat("/home/grochmal/.config/fontconfig/conf.d", 0x7ffff95fbbc0) = -1 ENOENT (No such file or directory)
access("/home/grochmal/.config/fontconfig/conf.d", R_OK) = -1 ENOENT (No such file or directory)
access("/home/grochmal/.config/fontconfig/conf.d", R_OK) = -1 ENOENT (No such file or directory)
stat("/home/grochmal/.config/fontconfig/fonts.conf", 0x7ffff95fbbc0) = -1 ENOENT (No such file or directory)
stat("/home/grochmal/.config/fontconfig/fonts.conf", 0x7ffff95fbbc0) = -1 ENOENT (No such file or directory)
access("/home/grochmal/.config/fontconfig/fonts.conf", R_OK) = -1 ENOENT (No such file or directory)
access("/home/grochmal/.config/fontconfig/fonts.conf", R_OK) = -1 ENOENT (No such file or directory)
access("/home/grochmal/.fonts.conf.d", R_OK) = -1 ENOENT (No such file or directory)
access("/home/grochmal/.fonts.conf.d", R_OK) = -1 ENOENT (No such file or directory)
access("/home/grochmal/.fonts.conf", R_OK) = -1 ENOENT (No such file or directory)
access("/home/grochmal/.fonts.conf", R_OK) = -1 ENOENT (No such file or directory)
stat("/home/grochmal/.local/share/fonts", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
open("/home/grochmal/.local/share/fonts", O_RDONLY|O_CLOEXEC) = 4
stat("/home/grochmal/.local/share/fonts", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
open("/home/grochmal/.local/share/fonts", O_RDONLY|O_CLOEXEC) = 4
open("/home/grochmal/.local/share/fonts", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 5
stat("/home/grochmal/.local/share/fonts/HomemadeApple.ttf", {st_mode=S_IFREG|0644, st_size=110080, ...}) = 0
open("/home/grochmal/.local/share/fonts/HomemadeApple.ttf", O_RDONLY) = 6
stat("/home/grochmal/.local/share/fonts/TTF", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("/home/grochmal/.fonts", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
open("/home/grochmal/.fonts", O_RDONLY|O_CLOEXEC) = 4
stat("/home/grochmal/.local/share/fonts/TTF", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
open("/home/grochmal/.local/share/fonts/TTF", O_RDONLY|O_CLOEXEC) = 4
stat("/home/grochmal/.local/share/fonts/TTF", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
open("/home/grochmal/.local/share/fonts/TTF", O_RDONLY|O_CLOEXEC) = 4
open("/home/grochmal/.local/share/fonts/TTF", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 4
stat("/home/grochmal/.local/share/fonts/TTF/HomemadeApple.ttf", {st_mode=S_IFREG|0644, st_size=110080, ...}) = 0
open("/home/grochmal/.local/share/fonts/TTF/HomemadeApple.ttf", O_RDONLY) = 5
stat("/home/grochmal/.fonts/Type1", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
open("/home/grochmal/.fonts/Type1", O_RDONLY|O_CLOEXEC) = 4

And PhantomJS went to the font directories and loaded them!

I do not have a ~/.config/fontconfig/fonts.conf which may be needed for CJK fonts (because those may need some actual configuration), but you can copy a file from /etc/fonts/conf.d/* (notably some nonlatin font, to get a sample configuration).

Yet, you can probably get away with most fonts by simply dropping them into ~/.local/share/fonts/TTF and then running fc-cache.

Disclaimer: An old RedHat (5 for sure, not sure about 6) may not be using fontconfig, that's why I included the PFB font in the example. In that case you need to use ttmkfdir and mkfontdir to generate the font.scale and font.dir files.

References:

Related Question