Fonts in node-canvas

Adam Hooper
3 min readMar 14, 2017

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

Pango handles the tricky task of putting text in the right place. If you use node-canvas without Pango, you’ll get blocky and uneven text.

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

There’s so much stupid history here.

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

Mac OS X and Windows don’t use FontConfig by default, and FontConfig is how we’re configuring fonts. Set the environment variable PANGOCAIRO_BACKEND to fontconfig, either in your shell (Bash: PANGOCAIRO_BACKEND=fontconfig FONTCONFIG_PATH=assets/fonts node render.js) or within your script (Node: process.env.PANGOCAIRO_BACKEND = 'fontconfig').

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

Use ctx._setFont()

The <canvas> way of setting a font is to call ctx.font = 'bold 30px Helvetica';. node-canvas mimics that, but it doesn’t do what your browser does and it can fail (for instance, if your font names contain commas).

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

All that’s left is to check font names (which are not filenames), weights, styles, sizes … and the environment variables you created above.

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

At this point, you probably realize that node-canvas is nothing like HTML5 <canvas>. Oops. But there is a cross-platform way to use fonts.

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

Sign up to discover human stories that deepen your understanding of the world.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Adam Hooper
Adam Hooper

Written by Adam Hooper

Journalist, ex software engineer

Responses (2)

What are your thoughts?