Do you use node-canvas to draw images from JavaScript using Cairo?

Well, you’ll want to select a font, and that’s Really Hard.

node-canvas 2.0 will have a ctx.registerFont() method that may supplant these instructions. In the meantime, here’s what you should do:

Here’s the nitty-gritty, for Linux and Mac OS X.

Make sure you’re using pango-cairo

node-canvas can install without Pango. Avoid that.

Follow the node-canvas instructions: install pango before installing node-canvas. On Mac OS X, brew install pango. On Ubuntu, apt install libpango1.0-dev.

If you installed node-canvas after installing Pango, reinstall node-canvas: rm -r node_modules/canvas && npm install canvas

Set FONTCONFIG_PATH and place fonts.conf there

When you open a web page that asks for “Helvetica” but you don’t have “helvetica.ttf” installed, your web browser may pick “arial.ttf” instead. There’s a font cache in your operating system that matches each font name to a file on disk.

node-canvas pretends to be an HTML5 <canvas>, so it mimics this absurdity. You’ve (hopefully) copied each font file into your project’s source, but by default, node-canvas won’t look there.

Configure the font cache, FontConfig (it’s cross-platform), to look for fonts in your project directory:

  1. Create a directory, assets/fonts, and copy/paste the fonts.conf from above into it.
  2. Copy all font files for your project into assets/fonts/. They should be .ttf and .otf files.
  3. Before using node-canvas, set the environment variable FONTCONFIG_PATH to point to the directory that holds fonts.conf. You can either do this in your shell (Bash: FONTCONFIG_PATH=assets/fonts node render.js) or within your script (Node: process.env.FONTCONFIG_PATH = './path/to/assets/fonts').

You can’t have .. in your FONTCONFIG_PATH. You may usesomething like process.env.FONTCONFIG_PATH = require('path').resolve(__dirname, './assets/fonts') to remove all .. directories.

If you misconfigure FontConfig, node-canvas will use your system fonts. That means different computers will render differently.

Force FontConfig on Mac OS X and Windows

If you don’t set PANGOCAIRO_BACKEND, your font configuration won’t apply everywhere. Different computers will render differently.

Use ctx._setFont()

Skip the middle-man. Use ctx._setFont(weight, style, size, 'px', family) every time.

Never use ctx.addFont(): it circumvents Pango.

Debug with FC_DEBUG, fc-match, fc-list and pango-view

First, test you’re using FontConfig. Set the environment variableFC_DEBUG=8191 before you run your script. Your script will output screens and screens of font debugging information. If it doesn’t, you aren’t using pango-cairo and FontConfig: that’s your bug. Fix it.

Once you’ve confirmed you’re using FontConfig, you can set FC_DEBUG to various values for all commands in this section.

Make sure a font name maps to the correct font file:

FONTCONFIG_PATH=assets/fonts fc-match 'Proxima Nova Condensed'

The first line of output is the font file pango will use for the given name.

To find a list of candidate names, run this:

FONTCONFIG_PATH=assets/fonts fc-list

Finally, use pango-view to quickly check that a font renders correctly. Here it is on Linux:

FONTCONFIG_PATH=raw-data/fonts pango-view --font='ProximaNova Condensed Extra Bold 20' -t 'Hello, world'

On Mac OS X, you’ll need X to make pango-view actually show something. brew cask install xquartz && brew reinstall cairo --with-x11 --build-from-source && brew reinstall pango --with-x11 --build-from-source && brew reinstall imagemagick — with-x11 — build-from-source (in that order), launch xQuartz, then prepend PANGOCAIRO_BACKEND=fontconfig to the command above.

My parting thoughts

Incidentally, you can use all this advice (minus the _setFont()) in Ruby and Python code, too.

Journalist, ex software engineer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store