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 @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.
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:
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
Grab your True Type Font files. For this example, I went to Google Fonts and downloaded all of the Roboto
Then, head over to the Online Font Converter or any font-converter of your choice. Select
.pfb and upload your
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 email@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
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
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 | 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.