Installing fonts on Amazon Linux AMIs for PhantomJS proper rendering
How to install fonts on Amazon Linux AMIs to ensure they render properly when using PhantomJS.
PhantomJS is simultaneously a wonderful and terrible piece of software.
I opted to use phantomjs
over many other alternatives to create PDFs from HTML generated by a Rails 5.1 web-app.
Locally, everything worked after getting through some PhantomJS quirks. However, once I deployed to the stage environment (an AWS EC2 t2.small instance running an Amazon Linux AMI) for testing, things got a little weird.
I had been importing the pdf-specific fonts in a print.scss
stylesheet.
// print.scss
@import url('https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,500,500i,700,700i');
Finally, when the PDF service ran the pdf generation script, bin/phantomjs rasterize.js
, this happened.
Expected Output:
Actual Output:
You can notice inconsistent kerning and font weights in this simple example, but it looks much worse on a paragraph of text and large headers.
After some digging, there are a number of annoying and related WebFont / True Type rendering bugs in 2.X
versions of PhantomJS:
- https://github.com/ariya/phantomjs/issues/14853
- https://github.com/ariya/phantomjs/issues/11413
- https://github.com/ariya/phantomjs/issues/10592
- https://github.com/ariya/phantomjs/issues/14450
None of the "work-arounds" in these issues actually worked for me. So, after many hours of playing wack-a-mole, I eventually solved the rendering problem
by converting the TrueType Webfonts imported through Google Fonts into a Type1
font installed physically on the server.
Now, we'll walk through that process so that others won't have to go down the rabbit-hole I did.
Step 1: Convert your .ttf
font files to a .pfb
format.
Grab your True Type Font files. For this example, I went to Google Fonts and downloaded all of the Roboto .ttf
files.
Then, head over to the Online Font Converter or any font-converter of your choice. Select .pfb
and upload your .ttf
files.
Download the generated .zip
folder and extract all of the contents.
Finally, move all of the generated .pfb
files into a new Roboto
directory to keep the folder structure simple.
Step 2: Get your converted font on to the server.
scp -r ~/Downloads/Roboto username@example.com:~/
Step 3: Install the the font.
Now we'll move the fonts into the proper directory. You'll need sudo
permissions to move anything into /usr/share/fonts/*
:
sudo mv ~/Roboto /usr/share/fonts/default/Type1
There are alternative locations you could place the font, like ~/.fonts
. However, the major benefit of putting it in usr/share/fonts
is that it make Roboto
available to all of this server's users, instead of this particular user.
Also, if you take a gander in the Type1
sub-directory, you'll notice a number of different filetypes, including afm
and pfb
. I found through testing that I only needed pfb
files. However, you might want to try adding afm
if you have trouble getting the font to render properly.
Step 4: Update the font cache.
$ fc-cache -fv
/usr/share/fonts: caching, new cache contents: 0 fonts, 3 dirs
/usr/share/fonts/default: caching, new cache contents: 0 fonts, 2 dirs
/usr/share/fonts/default/Type1: caching, new cache contents: 35 fonts, 1 dirs
/usr/share/fonts/default/Type1/Roboto: caching, new cache contents: 12 fonts, 0 dirs
/usr/share/fonts/default/ghostscript: caching, new cache contents: 13 fonts, 0 dirs
fc-cache: succeeded
You should see fc-cache: succeeded
at the end of this output. Now double-check that Roboto is available for use with the fc-list
method:
$ fc-list | grep Roboto
Roboto:style=Italic
Roboto Black:style=Italic
Roboto:style=Bold
Roboto Black:style=Regular
Roboto Medium:style=Regular
Roboto:style=Regular
Roboto Light:style=Italic
Roboto Thin:style=Italic
Roboto:style=Bold Italic
Roboto Medium:style=Italic
Roboto Thin:style=Regular
Roboto Light:style=Regular
Step 5: Use it.
Now you can setup your font stack for phantomjs
to use:
// print.scss
$font-family-sans-serif: Roboto, "Helvetica Neue", Arial, sans-serif !default;
$ phantomjs rasterize.js my-test-file.pdf
Sweet. Problem solved.