Friday, October 19, 2018

How to get a custom font into an AWS Lambda

I had a problem.  For the longest time, I've been able to avoid localization.  Back in 2008, I shipped Spore with 22 different languages.  You haven't lived until you deal with kerning issues while shipping Thai inside a video game UI.

Enter my most recent year with Viber.  A company founded in Tel Aviv and owned by a Japanese multinational, it's easily the most international company I've ever worked for.  Which brings me to my latest technical challenge:  


Russia

I don't remember localizing Cyrillic before.  And the wrinkle I needed to deal with is that fact that in 2013, the currency symbol changed to .  It was adopted in 2014 by the Unicode Technical Committee and released as part of Unicode 7.0 in June of that year. Unicode nerds rejoiced! 

Do you have any idea how often default font sets get updated in standard cloud-based linux server images?  Neither do I, but I'm guessing not often enough.  I can tell you that Unicode 7.0 is not available on any default Amazon Linux image I've found.  Which brings me to my next part of the mine field: Lambdas

What you need to know about Lambdas is that you as a developer don't really get to control the whole server instance in the cloud.  You get a tiny bit of developer space to drop your code into.  How the heck am I going to install an additional font set onto a Lambda?

Enter fontconfig.

Bless you, fontconfig! It makes this kind of thing possible.  I owe my success with this week-long odyssey to the makers and maintainers of it.  fontconfig is a library that allows you to do exactly what its name implies: 


Configure where the operating system goes to look for fonts

If you've ever printed a document and you start seeing these funny question marks all over the place, you know the kind of fun I'm talking about:  


 � � � �

Which is sort-of-exactly what I was getting from my image-compositing program built with Node and running on a Lambda:
And with a little magic with fontconfig, I could generate it correctly:
Amazing!  How did I pull this off?  Are you having this problem too?  Let me outline my journey for you:

Step One: Go Down a Blind Alley


A helpful coworker gave me this link.  It showed promise.  This person was trying to get CJK (Chinese, Japanese, Korean) characters to render inside a headless chrome instance running inside an AWS Lambda.  I only sorta know what he/she is talking about.  But I see LAMBDA and FONT and CHINESE CHARACTERS. So I know we are in the ballpark.

I installed docker so I could run inside a container that closely emulates the environment that Lambda runs in.  I download, configure, make and make install fontconfig from source. Ugh. I find a google font set that contains my missing cyrillic ruble currency symbol. (hey look: Bitcoin made it too!)

It turns out that I needed to do almost none of this.  Well, I did need to download the True Type Font set "Roboto" from Google.  But I digress.

Step Two: Try The Simple Way

My lambda code is written in Node. I figured that when I zipped up my existing code to upload to AWS Lambda with my additional special font, it would need to look something like this:

/my_lambda_code
    index.js
    /node_modules
    /roboto  (my new font folder)

But how do I tell me Node program to use this font?  I was at a loss until this Stack Overflow answer gave me what I needed.

What's important is what is INSIDE the "/roboto" subdirectory:

/roboto
    Roboto-Black.ttf
    Roboto-BlackItalic.ttf
    Roboto-Bold.ttf
    Roboto-BoldItalic.ttf
    Roboto-Italic.ttf
    Roboto-Light.ttf
    Roboto-LightItalic.ttf
    Roboto-Medium.ttf
    Roboto-MediumItalic.ttf
    Roboto-Regular.ttf
    Roboto-Thin.ttf
    Roboto-ThinItalic.ttf
    fonts.conf

The fonts.conf file looks like this:

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
  <dir>./roboto/</dir>
  <cachedir>/tmp/fonts-cache/</cachedir>
  <config></config>
</fontconfig>

This tells fontconfig that "roboto" is the only font available.  Use it no matter what.  The only step missing is how to tell the Node runtime to use this config file. Once again the Stack Overflow user Jeremy Green had my back.  All I needed was one environment setting in my Node script:

process.env.FONTCONFIG_PATH='./roboto'

Boom.  I'm a Unicode 7.0 rendering madman.

Step Three:  I lied about how easy this was

I really left a bunch of clues out.  Here is a Github repo that adds SVG support to the existing ImageMagik installed on an AWS Lambda Linux VM.  It uses the same fontconfig trick.  That got me on the right path.  Installing a bunch of packages and compiling from source on a docker image was useful for learning about these underlying technologies but ultimately unnecessary for solving my problem.  Most of my professional life in software is like this.  Wandering down paths I have little expertise in.  Making huge time wasting mistakes.  This journey was fun and rewarding.  Which was so unusual I felt compelled to blog about it.  Thanks for reading.

6 comments:

  1. Nice! Thanks for writing this post, worked perfectly.

    ReplyDelete
  2. Thanks, this helped and saved my time a lot

    ReplyDelete
  3. Thanks for the amazing blog you have provided us. It is so informative and interesting. As nowadays the global market is evolving and businesses are growing, so there is so much chance of debt. Therefore you must consult a debt collection company.

    ReplyDelete