Fonts in node-canvas
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:
- Create a directory,
assets/fonts
, and copy/paste thefonts.conf
from above into it. - Copy all font files for your project into
assets/fonts/
. They should be.ttf
and.otf
files. - Before using node-canvas, set the environment variable
FONTCONFIG_PATH
to point to the directory that holdsfonts.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.